home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr50
/
langwn23.zip
/
LANGWIN.DOC
< prev
next >
Wrap
Text File
|
1993-02-11
|
183KB
|
3,268 lines
-----------------------------------------------------------------------------
LangWin Version 2.3 (C) Copyright Allen L. Lang, 1993 ALL RIGHTS RESERVED
-----------------------------------------------------------------------------
LangWin is a QuickBASIC program development toolkit. It contains a library of
functions and subroutines that can be used to easily create standard Graphical
User Interface (GUI) features in your compiled text-mode QuickBASIC or BASIC
PDS programs (sorry, LangWin cannot be used with the QBASIC interpreter in DOS
5.0).
GUI features supported include: opening/closing windows with drop-shadows,
scrollable text, push buttons, click boxes, dialog boxes, editable input
fields, etc. LangWin's GUI routines support both mouse and keyboard input.
Windows created by LangWin can be moved and resized (via mouse only). LangWin
also contains stand-alone routines that can be used to add mouse support to
your QuickBASIC programs that do not use LangWin. Mouse features include:
initialization, get/set position, get button status, hide/show cursor, set
horizontal/vertical limits, etc. Finally, LangWin has several functions that
allow you to get the default drive and directory, change the default
drive/directory, and extract all file/sub-directory names from the current
directory. All features are documented below.
Let's get the formalities out of the way before exploring LangWin's
capabilities ...
LICENSE
-------
All rights are reserved. LangWin is licensed free of charge. LangWin may be
copied and distributed provided: that all files are copied from this package,
as is, and in their entirety, and that no fee is charged for such copies beyond
the expense of duplication, media, and distribution. The author's rights extend
to all copies so made. Permission is granted to distribute all programs
developed using LangWin.
WARRANTY
--------
LangWin is not covered by any warranty, expressed or implied. The author will
not be liable for anything that happens as a result of any use of, or the
inability to use, LangWin in your particular application. Stated another way:
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
LAW. THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
==========================================================================
Phew, I hate that part. Now let's get to the good stuff: what is LangWin
and how can you use it to create some great looking BASIC programs ...
--------------------
LANGWIN USER'S GUIDE
--------------------
--------
CONTENTS
--------
0.0 CHRONOLOGY OF CHANGES IN LANGWIN'S VERSIONS
0.1 Notes for Those Upgrading From Previous Versions of LangWin
1.0 REQUIREMENTS AND RESTRICTIONS
2.0 GETTING STARTED
3.0 DOING "BASIC" WINDOWS
3.1 Initialization
3.2 Some Simple Windows
4.0 USING LANGWIN'S GUI
4.1 Close Icon
4.2 Moving, Resizing, and Minimizing the Active Window
4.3 Focus
4.4 Changing Focus (window & object focus)
4.5 Scrolling Text
4.6 Selecting Scrollable Text
4.7 Push Buttons
4.8 Check Boxes
4.9 Input Fields
4.10 No mouse
5.0 DATA STRUCTURES AND GLOBAL VARIABLES
5.1 MaxWindows
5.2 MaxButtons
5.3 MaxTextLines
5.4 MaxTextWins
5.5 AnyWinOpen
5.6 CurWinPtr
5.7 Window numbers vs window handles
5.8 SaveText
5.9 WinParms
5.10 button handles
5.11 ButtonsText
5.12 ButtonsData
5.13 UserHotKeys
5.14 WinNum
6.0 ADVANCED WINDOWS
6.1 Adding Frills to Your Windows
6.2 Window Modes, Shadows, and Movement
6.3 Using Wallpaper Windows (Info Only Window and
Multiple Lists In One Window)
6.4 Modifying Scrollable Text While Its Window Is Open
6.5 Changing The Entire Scrollable Text Array While Its Window Is Open
6.6 Modifying the Contents of an Input Field While Its Window Is Open
6.7 Determining What Had Focus When WinEvent Returns Control
6.8 Nesting Calls to WinEvent
6.9 Deactivating and Activating Buttons
6.10 Giving a Specific Button Focus When It Is Created
6.11 Is A Given Window Open
6.12 Manually Giving a Window Focus (i.e., via program control)
6.13 State of Check Boxes
6.14 Run Time Errors
6.15 Testing for Color, B/W, EGA, or VGA
6.16 Colors, Attributes, and Palettes
6.17 WaitTicks
6.18 Video Pages
6.19 Using Non-Text Mode Graphics With LangWin
6.20 Closing Windows
6.21 Modifying Static Text Created With ShowWinText
6.22 "Time Out" Feature of WinEvent for Interrupt Buttons
6.23 Dynamically Adding Entries to a Visible List of Scrollable Text
7.0 MOUSE ROUTINES
8.0 DIRECTORY AND FILE ROUTINES
9.0 COMING ATTRACTIONS
10.0 CONTACTING THE AUTHOR
-----------------------------------------------
0.0 CHRONOLOGY OF CHANGES IN LANGWIN'S VERSIONS
-----------------------------------------------
You're right! I added this section after the user's guide was complete, and I
didn't feel like re-numbering all following sections (I'm using a plain vanilla
text editor). So, this section gets number zero. I guess that means the next
section that needs to be inserted must have a negative number!
Major changes implemented in LangWin's Versions:
1.0 Never released. Finally got LangWin packaged, but I thought of too many
additional features that I wanted to include. [2/92]
1.1 First version of LangWin released. [4/28/92]
1.2 - Simplified the license statement.
- Updated Section 6.5 of user's guide.
- Significant performance improvement in window open/close by
writing/reading directly to/from video buffer (DEF SEG=&HB800).
(so don't relocate/reuse the upper memory block at segment &HB800).
[5/23/92]
2.0 Major re-write. Removed the restriction that "locked" the mouse onto
the current window. With 2.0, you can open multiple windows, mouse
to any visible window, click on any button, scroll any text, etc.
(rather than being restricted to just the current window in focus).
I've also added several new window features such as shadowless,
unmovable, modal (you must close it before any other window can be
selected), immediate close (clicking on another window will automatically
close it), and wallpaper (background only). These new features require
a slightly different programming technique than LangWin 1.x, so most
code developed for LangWin 1.x will need to be updated (new calling
parameters for some routines and slightly new logic in the WinEvent
loop). Sorry, but after you start using 2.0, I think you'll agree
that the hassle of re-coding was worth the new features provided.
The new features are described in this User's Guide.
Both QuickBASIC and BASIC PDS are now supported.
I've also moved the online documentation for all LangWin's routines
from a stand-alone program (which required you to exit QB and run the
program to get info on a particular routine), to a module (WINHELP.BAS)
that can be loaded into QB with your program under development. Thus,
you need only hit the F2 key while developing with LangWin in the QB
environment to get online help for any routine.
[10/25/92]
2.1 Window resizing and minimizing added.
ChangeButtonFocus routine added to allow a specific object to be given
focus when a window is first opened (i.e., creating a default button).
Section 6.10 has more details and examples.
Some parameters in LangWin's data structures were changed
(section 5.0 has most current description).
ActivateButton routine no longer needs a string variable with the
Buttons's text. The Button's original text is remembered when it is
deactivated (DeactivateButton) and automatically restored.
If converting from V2.0, remove the string parameter from calls
to ActivateButton or you'll get a parameter mismatch error.
[11/21/92]
2.2 Fixed misc bugs.
- Buttons at top of screen could not be moved or resized.
- When minimizing and restoring window to original size,
window would be 2 columns larger than original.
- Code in WinEvent required left mouse button to be down
at the actual time the mouse was polled (in order to "recognize" a
click on the left button). On slow machines, it was possible
for the left button to have been clicked and released at the point in
time when the mouse was polled. Symptom was that left mouse button
was ignored. Code now only tests to determine if left mouse
button had been pressed any time since the last time mouse was polled
(current state of the left button at the time the mouse is polled
is no longer tested). The state of the right button is still
tested at the time the mouse is polled (to distinguish between
a left click, a right click, and both buttons clicked).
The mouse remains active when an input field is selected for data entry.
- Mouse can be used to place text cursor anywhere in the input field.
- Mouse can be used to exit input mode by clicking anywhere outside
the input field (including clicking on wallpaper, another window,
another button, another inputfield, etc.).
The input field can be used for password entry. All text entered will
be displayed with the * character; the actual text entered will be
saved in ButtonsText(handle) - just as any input field's text.
Use a negative value for the input field's length (in MakeInputField) to
cause the input field to be used in this way. An alternative
technique, available in previous releases, would be to define the input
field with the same values for foreground and background colors. In this
case, nothing will be visible when text is entered.
[12/12/92]
2.3 Several new hints & tips were added to Section 6.0 (Advanced Windows)
of this User's Guide (i.e., 6.18 - 6.23); other sub-sections were updated
(6.3.2 and 6.16). Previous users of LangWin should scan all of
Section 6.0 and read the new sub-sections.
A new option ("time out") was added to WinEvent. With the time out option
enabled, WinEvent will automatically return control to your program after
0.5 sec if no events are detected. The time out option can be used to
implement an "interrupt" button for long running tasks. See Section 6.22
and SAMPLE05.BAS for additional details and sample code.
A new function was added (GrowScrollText) that allows you to dynamically
add entries to a visible list of scrollable text (i.e., after the
scrollable text window has been opened). This can be used to
give the user real-time feedback while a long running task is underway.
For example, suppose you are searching an entire hard disk for all files
that match a given specification. As each file name is found, it can be
added to a visible list of scrollable text in the current window.
Section 6.23 contains additional details and SAMPLE05.BAS illustrates
the use of GrowScrollText.
Most of the SAMPLE programs included on the distribution disk were
cleaned up. Standard code is used in the main modules to initialize
(see Section 3.1 for examples). SAMPLE04.BAS was re-written (it still
performs the same task). SAMPLE05.BAS is new with V2.3.
The GetFileNames function (see Section 8.0) has been modified.
This functions places all file or directory names that match a specific
criteria into a string array (whose name is passed as a parameter).
Previously, this string array had to be DIMed to a given size, and
if the array was filled, an error code was returned. With V2.3, the
size of string array is automatically adjusted by GetFileNames to
hold all of the names that match the given criteria. The string array,
however, MUST be DYNAMIC or a "duplicate definition" error will occur.
In addition, a new option was added to GetFileNames. Previously, only
"regular" file or directory names could be extracted (i.e., not
read only, hidden, or system). The new option allows ALL names that
match the criteria to be extracted (both files and directories). In this
case, the name is prefixed with a one byte string containing the value of
the file or directory's attribute. The bits in this byte can be tested to
determine whether the name corresponds to a file or directory, and
whether it's regular, hidden, system, read-only, etc. Before displaying
the name, the attribute byte must be removed, otherwise you'll get some
strange values as the file or directory's first character. See
WINHELP.BAS for more details on the GetFileNames function along with a
description of the attribute byte.
OpenScrollWindow has been modified. This function displays the contents
of a string array in a scrollable window. In previous releases, if
UBOUND of this array was greater than the MaxTextLines global variable,
a run-time error would terminate your program when OpenScrollWindow
was called. As of V2.3, if UBOUND of the string array > MaxTextLines,
then only the first MaxTextLines of the array will be displayed
(run-time error will not occur). To let your user know that this
condition has occurred, the last line in the scrollable window will
be set to the string: "(Incomplete List)"
RefreshScrollText has been modified. This subroutine is used to
dynamically change the scrollable text displayed in a window created via
OpenScrollText. A string array is passed to RefreshScrollText which
contains the new scrollable text to be placed into the current window.
Similar to the modification made to OpenScrollWindow mentioned above,
if UBOUND of this string array is greater than MaxTextLines, then only
the first MaxTextLines will be displayed with the last line set to the
string: "(Incomplete List)". A run-time error will no longer be
generated if UBOUND of the string array is greater than MaxTextLines.
When moving a window, you can no longer attempt to place it off of the
screen (which used to cause an error message to be displayed). When
moving a window (by dragging upper left corner with left mouse button),
the mouse cursor will be limited (can't be moved off of the screen).
If the window has a shadow, the limit set for the mouse cursor will
have two columns on the right and one row at bottom reserved for
the area where a shadow will be displayed.
Any video page available on your system can now be used with LangWin
(previous versions of LangWin only supported page 0); however, the
SAME video page MUST be used for all windows/objects created with
LangWin. See Section 3.1 for a discussion on initializing the video
page with the SCREEN command, and see Section 6.18 for a discussion
on using video pages. With V2.3, you MUST place the SCREEN command
BEFORE the call to LangWinInit (Section 3.1 has more details).
In some cases, moving small (e.g., minimized) windows could result in the
lower right "move" cursor not being erased. This bug was corrected (it
was never reported, I found it when testing the new code to support
video pages beyond page 0). The bug was caused by not hiding the mouse
cursor before doing i/o to the screen (i.e., you can't change the
character "under" the mouse cursor without first hiding the cursor).
After finding the above bug, I looked for other similar problems caused
the same omission (i.e., not hiding the mouse cursor prior to writing to
the screen). RefreshScrollText had a similar bug. If the mouse cursor
happened to be at the very top of the scroll slider area, then the
contents of the screen "underneath" the mouse cursor would not be updated
correctly when the text was refreshed. This bug was also corrected.
The DeactivateButton routine (introduced in V2.0) can be used to make a
button "inactive". The button will still appear in the window, but its
text will be cleared, and it cannot be selected (until reactivated with
the ActivateButton routine). If a window with an "inactive" button was
resized by moving the right side of the window to the left, the inactive
button's length was incorrectly computed, and in some cases the inactive
button could end up being displayed beyond the right boundary of the
resized (smaller) window. This bug was fixed. When a window is made
smaller, all objects will be compressed, and will eventually "disappear"
if the window is made small enough (they will reappear when the window is
enlarged).
The Page-Down key can be used to display the next page of a scrollable
list of text. However, if the actual scrollable list had fewer rows than
the area allocated for scrollable text when the window was opened, and
the Page-Down key was used, the text was not displayed correctly. The
last physical line of the text area, rather than the last line of actual
text, was highlighted. This highlight then remained on the screen after
a subsequent Page-Up. This bug has been fixed.
Successive calls to ActivateButton would actually toggle between the
active/deactive state. Similarly for successive calls to
DeactivateButton. These bugs have been fixed. Now, successive calls to
ActivateButton (DeactivateButton) will all result in the button
remaining active (inactive).
[3/20/93]
0.1 Notes for Those Upgrading From Previous LangWin Versions
------------------------------------------------------------
This section contains some information that is repeated from the previous
section. For those who read the documentation from "cover to cover", please
excuse the repetition. For others who skip around, I've included some of the
key changes (as described in the previous section):
For LangWin 1.x users: one change you'll have to deal with is that several
routines now have additional calling parameters, failure codes are negative
(i.e., in cases of failure, the value returned is the same as in V1.x, but the
number is negative rather than positive), and some new failures are detected.
Load the online help facility (WINHELP.BAS) into QB and scan the documentation
for each routine. I've tried to include a "V2.0 Changes" section to help you
spot them. Routines that should be examined for changes include: BlankWin,
OpenScrollWindow, WinEvent, ReShowText, and ReShowPage. New routines include:
NewFocusWindow, RefreshScrollText, IsWinOpen, ActivateButton, DeactivateButton,
ChangeDir, ChangeDrive, GetCurDir$, GetCurDrive$, and GetFileNames. Be sure to
scan ALL routines in WINHELP.BAS to review new features, functions, parameters,
return codes.
Another change for V1.x users is that it requires a slightly different (easier)
programming technique. With 1.x, you needed a separate call to WinEvent for
each window opened. Starting with 2.0, you can get by with one call to WinEvent
for your entire program! Although, for complex programs with many menus and
nested windows, you might want to have a separate subroutine for each menu,
with one call to WinEvent in each subroutine. Be careful not to nest calls to
WinEvent too deep or you'll run out of BASIC's stack space.
Starting with v2.0, BlankWin and OpenScrollWindow now return a unique NUMBER
associated with the window created (in V1.x, these routines returned a handle,
see Section 5.7 for a discussion of the difference between these two concepts).
This number will be used to determine which window had focus when an event
occurred. As in V1.x, save the value returned by these routines in a unique
variable for each window.
WinEvent handles all scrolling, mouse, and keyboard events. Starting with V2.0,
WinEvent will detect when you mouse to a different window and click on it. In
this case, the new window will be given "focus" (by making visible any hidden
parts of the new window that are "under" other overlapping windows). When you
click on an object (button, scrollable text, close icon), WinEvent makes the
corresponding window current (gives it focus). In addition, the WinEvent
function returns a value equivalent to the number of the window with focus
(this is the number assigned to the window when it was created by BlankWin or
OpenScrollWindow). WinEvent takes one output parameter which is set to an code
that defines the type of action that was selected in the window (1=close;
2=scrollable text selected; 3=button selected). The action code parameter is
given the same value that the WinEvent function itself returned in LangWin 1.x.
Between the window number and action code returned, you can determine what
event took place and where it happened.
With LangWin 2.0, several new window modes are supported. See Section 6.2 for
details.
With LangWin 2.0, both QuickBASIC and BASIC PDS are supported.
With V2.1, you can now resize and minimize windows (see section 4.2). In order
to implement this function, non-scrollable text (created with ShowWinText) has
to be saved in LangWin's data structures (so it can be re-displayed). The
MaxButtons global variable (see sections 3.1 and 5.2) must now be large enough
to not only count all buttons, but also all lines of non-scrollable text. If
you are converting from a previous version of LangWin, it's likely that you'll
have to increase the value of MaxButtons (by the number of lines of non-
scrollable text that could be simultaneously visible on all open windows).
If MaxButtons is not large enough, the corresponding "make" routines that
create objects will return an error code. New with V2.1, ShowTitle and
ShowWinText will also return a code if there's no room in the data structures
to save the corresponding title or static text. If you do not check for these
error codes, the symptom you'll see is that objects you expect to see (buttons,
static text, titles, etc.) will not be visible.
With V2.1, ActivateButton routine no longer needs a string variable with the
Buttons's text. The Button's original text is remembered when it is deactivated
(DeactivateButton) and automatically restored when activated. If you used the
ActivateButton routine in LangWin 2.0, just remove the string variable from the
calling parameter list (otherwise you'll get a parameter mismatch error). If
you used the string variable in ActivateButton to change the button's text
prior to activating, my apologies (you can't do that anymore) but that's not
how the parameter was intended to be used. If you need to change the button's
text prior to activation, then change the contents of ButtonsText(han), where
han is the button's handle. Be careful not to make the button's new text longer
than the original button's length - which is saved in ButtonsData(han,4). See
Section 5.11 for additional details on the button's text.
With V2.3, you MUST place the SCREEN command BEFORE the call to LangWinInit.
See Section 3.1 for additional details.
With V2.3, OpenScrollWindow and RefreshScrollText no longer generate run time
errors if UBOUND of string array (containing scrollable text) is greater than
MaxTextLines (i.e., the maximum number of text lines DIMed in LangWin's data
structures - see Section 3.1 for details on defining MaxTextLines). Instead,
the number of text lines displayed will be truncated to (MaxTextLines - 1), and
the last line displayed will appear as: "(Incomplete List)"
Note that the contents of the string array are NOT modified, only the last line
displayed in the scrollable text window is changed. Prior to calling
OpenScrollWindow or RefreshScrollText, you can compare UBOUND of the string
array to MaxTextLines to detect this condition (or just allow the text lines
displayed to be truncated - in which case no special return code is provided).
With V2.3, a new option has been added to GetFileNames. Previously,
GetFileNames only returned "regular" file or directory names (i.e., not those
that were read-only, hidden, or system). The new option will return ALL names
(that match the file specification) along with an attribute byte (which is
included as the first character of the name). You must test the bits of this
attribute byte to determine if the name is a file or directory, and if it's
regular, hidden, system, read-only, etc. Then, before displaying the name, you
must remove the attribute byte (or the first character of the name will look
very funny). See the GetFileNames member in WINHELP.BAS for more details, along
with a description of the bits within the attribute byte.
With V2.3, GetFileNames will automatically REDIM the array passed to hold all
names that match the file specification. Previously, the array had to be pre-
DIMed large enough to hold all names (or an error code was returned if the
array was not large enough). Now, just pass an array that has been DIMed (1)
and GetFileNames will re-adjust the array to the correct size. Note, the array
MUST be DYNAMIC. LBOUND of the returned array will be 1. See WINHELP.BAS for
more information.
With V2.3, a new option ("time out") was added to WinEvent. With the time out
option enabled, WinEvent will automatically return control to your program
after 0.5 sec if no events are detected. The time out option can be used to
implement an "interrupt" button for long running tasks. See Section 6.22 and
SAMPLE05.BAS for additional details and sample code.
With V2.3, a new function was added (GrowScrollText) that allows you to
dynamically add entries to the bottom of a visible list of scrollable text
(i.e., after the scrollable text window has been opened, you can append text).
This can be used to give the user real-time feedback while a long running task
is underway. For example, suppose you are searching an entire hard disk for
all files that match a given specification. As each file name is found, it can
be added to a visible list of scrollable text in the current window. Section
6.23 contains additional details and SAMPLE05.BAS illustrates the use of
GrowScrollText.
Within this user's guide and the online help (WINHELP.BAS), I've tried to
include the corresponding LangWin version numbers when describing new
features/functions. Users of previous LangWin versions can scan for appropriate
character strings (i.e., 2.0, 2.1, etc.) to quickly find changes. However, I
would still encourage previous users to read this ENTIRE guide and ALL members
in WINHELP.BAS since, in many cases, entire sections have been re-written.
---------------------------------
1.0 REQUIREMENTS AND RESTRICTIONS
---------------------------------
LangWin is a GUI toolkit for QuickBASIC or BASIC PDS developers. You must have
one of these compilers to use LangWin.
Programs developed with LangWin are meant to run under DOS (see Windows
restriction later in this section). I developed LangWin using DOS 4.01 and did
some testing with DOS 5.0. LangWin was developed entirely in QuickBASIC, so it
should be compatible with earlier versions of DOS (but I did not do any
explicit testing with earlier DOS versions).
LangWin creates color text-mode (SCREEN 0) windows. You must have a color
monitor with EGA graphics or better (but also see Section 6.19).
A mouse is not required to take advantage of LangWin's GUI features, but
as you probably know, using any GUI from the keyboard PAINFUL!
LangWin is a quick library. It is object code, NOT source. (You CANNOT use
LangWin with DOS 5.0's QBASIC interpreter). I used QB 4.5 to develop LangWin
(which was written ENTIRELY in QuickBASIC to demonstrate how powerful this
language really is). LangWin is very similar to the User Interface (Menu,
Window, Mouse, and General) routines included with the BASIC Professional
Development System (PDS) V7.1. Actually, I think LangWin is easier to use, but
I might not be totally objective in my assessment!
Since the release of V2.0, there have been two "flavors" of LangWin's
libraries: one for QuickBasic (called LANGWIN.QLB and LANGWIN.LIB) and one for
BASIC PDS (called LANGWINP.QLB and LANGWINP.LIB). As previously mentioned, I
did all of my development and testing in QuickBASIC 4.5. I created the PDS
libraries and ran the samples under PDS 7.1, but I've not done extensive
testing in PDS, nor have I taken advantage of any PDS specific features
(LangWin source compiled clean under PDS the first time with no changes
required between the two flavors - except changing VARPTR to SSEG for variable
length strings). Beginning with LangWin V2.3, the compressed set of
distribution libraries were too large to fit on a 360K diskette. So I decided
to omit the PDS libraries (since most people have QuickBASIC). If the
distribution of LangWin that you have does not include the PDS libraries
(LANGWINP.*), and you would like the PDS versions, please contact me and I'll
send you a copy.
VBDOS? Serious competition for LangWin, but no comparison when you consider the
price (LangWin is free). I did load the sample programs and LangWin source into
the VBDOS environment and ran them successfully (no modifications were
required except for the VARPTR to SSEG changes made for PDS). I have not
included a VBDOS compatible quick library with this distribution. I'd be
flattered by anyone requesting a VBDOS compatible LangWin quick library, but in
that case: save the cost of VBDOS, keep QB or PDS, and use the quick libraries
included with this release. If you're gonna use VB, go ahead and use the GUI
tools that come with it (I'm considering some VB-like interactive graphical
tools for LangWin 3.0).
Windoze? LangWin's mouse interface will not work properly if DOS is run in a
window (not really a LangWin restriction, but the Int 33h interface to standard
DOS mouse drivers does not work in Windows which has its own mouse driver). So
if the code you develop with LangWin will be run in a Windows environment, and
you want to take advantage of LangWin's mouse interface, then you must load a
standard DOS mouse driver when you boot (via CONFIG.SYS or AUTOEXEC.BAT) BEFORE
starting WIN, and your code must run in full screen DOS under Windows, it
cannot run in a DOS window. This should not be a major restriction, but if you
must run in a DOS window, consider VBWIN for your development.
When using the LangWin quick library with the QB environment to develop code,
the more free (as opposed to installed) main memory you have the better. QB
consumes a certain amount of main memory when run. LangWin uses dynamic string
arrays and many string variables. Between the memory consumed by the QB
environment itself, and LangWin's strings, it is possible to encounter an "out
of string space" or "out of memory" message while developing under QB with the
LangWin library. (I developed LangWin in OS/2's DOS box which only had 470K
free on my system and often ran out of string space. When booting DOS, which
had 570K free, I did not have a problem).
You may not see a string space problem while developing unless your programs
get very large, have lots of comments, or use lots of strings. This is a
QuickBASIC environment restriction and relates to how much main memory QB
itself takes while loaded, and how it stores and manages strings. It's a good
idea to free up as much main memory as possible before calling the QB
development environment. Use the FRE (-1) and FRE ("A") commands from QB's
immediate window to determine the amount of free memory you have left from
within the QB environment. If you get an "out of string space" or "out of
memory" message while developing in the QB environment, use CLEAR in the
immediate window to reset all variables. If this does not help, save your code,
exit QB, and start QB again. If you still get "out of memory" or "out of string
space" try the following: reduce value of MaxWindows, reduce the "rows"
parameter in your WIDTH command, reduce the value of MaxTextWins, UNLOAD the
WINHELP module if it was loaded for online help (see the DOING "BASIC" WINDOWS
Section for more details). Programs compiled with the LangWin library will be
large (over 60K), but should not have a string space problem while running (of
course this too will depend upon how big your EXE is and how much free memory
you have).
LangWin allows up to 16 attributes (0-15) to be specified for colors. In order
to obtain 16 unique background attributes (usually there are only 8), blinking
foreground colors are disabled. This requires that foreground/background
attribute numbers be translated before using the COLOR command. Thus, when
using LangWin, your should NOT use the COLOR command directly in your code to
set foreground/background attributes. Instead, use LangWin's SetColor routine
(load WINHELP.BAS into QB to see details on all LangWin's routines). SetColor
takes two parameters (foreground and background attributes, between 0 and 15).
It will translate these appropriately and issue the COLOR command itself. If
you use the COLOR command instead of SetColor, you may get some unexpected
colors. Also see Section 6.15 for more information on colors in LangWin.
As of LangWin Version 1.2, a significant improvement in the speed of window
creation (open) and deletion (close) has been achieved by writing directly to
the video buffer (DEF SEG=&HB800). While the increase in speed is a benefit,
there are some disadvantages in writing directly to the buffer (rather than
using the standard DOS services). For example, if the location of the video
buffer were to move, LangWin would not know it and over-write whatever did
exist in the segment located at &HB800. Since many software products write
directly to the video buffer to improve performance, it's not too likely that
future releases of DOS will deliberately move the buffer. However, with DOS 5.0
(and several other memory management products), one can reuse upper memory
blocks (for TSRs, etc.) in order to free up as much of the first 640K as
possible. I haven't tried it, but I suppose it's possible to reuse the block
located at segment &HB800. In any case, if you do reuse the block at segment
&HB800, LangWin 1.2 (or later versions) will not work.
-------------------
2.0 GETTING STARTED
-------------------
Before you can begin developing with the LangWin quick library, you must place
LANGWIN.BI (include file), LANGWIN.QLB and LANGWIN.LIB (libraries) on your
hard disk, and tell QB where these files are (via the Options; Set Path menu).
I recommend placing them in the same directory as your other QuickBASIC
include files and libraries. LANGWIN.QLB contains QB.QLB and LANGWIN.BI
contains QB.BI. If your code defines a user data type as RegType or RegTypeX
and calls INTERRUPT or INTERRUPTX, you won't need to do anything extra beyond
using LangWin.
With V2.0, LANGWINP.QLB and LANGWINP.LIB are corresponding files for PDS;
LANGWIN.BI can be used for both QB and PDS. If you are using LangWin with PDS,
the following instructions also apply, just issue the corresponding PDS
commands, etc.
Select Options from the QB main menu, and Set Paths from the Options menu.
You'll see the current paths for executable, include, library, and help files
(these may all point to the same directory). You can then quit QB and place the
corresponding LangWin files into your defined directories for include files and
libraries. If your QB environment has no paths defined, then update the Set
Paths menu and place LangWin's file and libraries into the corresponding
directories. (For a quick start, just place the LangWin files in a working
directory and call QB /ah /L langwin from that directory.)
Once you have told QB where the LangWin files are (you'll only need to do this
once), then call the QB environment from your DOS prompt as follows (I assume
that "qb" is the name of your QB.EXE file and the directory where it resides
is on your DOS PATH):
qb /ah /L langwin (qbx /ah /L langwinp for PDS)
The /ah parameter tells QB to allow dynamic arrays > 64K in size
(it takes a lot of memory to save screen contents when opening windows).
The /L langwin parameter tells QB to load the LangWin quick library
(see your QuickBASIC manual for more information on Quick Libraries).
See Section 3.1 (and the sample programs distributed with LangWin) for typical
initialization code needed for LangWin.
I've included the contents of QB.BI in LANGWIN.BI and the contents of QB.QLB in
LANGWIN.QLB, so you DO NOT need to load the QB versions of these files
(similarly for the corresponding PDS libraries, they are included in LANGWIN.BI
and LANGWINP.QLB). If you used QB.BI and QB.QLB to call interrupts from your
QuickBASIC programs, all you'll need is the LangWin files and you'll still
be able to call interrupts.
-------------------------
3.0 DOING "BASIC" WINDOWS
-------------------------
Once you have called the QB environment with the LangWin library, you're ready
to begin doing windows. I'll cover "basic" windows in this section, and more
advanced techniques in a later section.
Note that most references to LangWin's routines in this document do not include
a complete description of the corresponding parameters and return codes. This
document attempts to show the concepts necessary to create a GUI environment
using LangWin. For a detailed description of every LangWin routine, including
parameters and return codes, load WINHELP.BAS (new with V2.0; included on your
distribution disk) into QB and hit F2. You can select any LangWin routine and
examine corresponding documentation. In order to reference the online help
routines, first OPEN an existing module under development (or create a new
one). Then select LOAD and load the WINHELP.BAS module (don't forget to UNLOAD
the WINHELP module before compiling your code!). While developing, if you need
to reference documentation for LangWin, just hit F2 and select the routine you
need. Routine names in WINHELP will end with a period, this is only to avoid
conflicts with the "real" routine names in LangWin. When you code the routine's
name, do not include the period. If you get "out of string space" or "out of
memory" errors, you may have to UNLOAD the WINHELP module.
3.1 Initialization
------------------
Before you can call LangWin's routines, they must be DECLARED, global arrays
and variables must be initialized, the screen must be set to text mode, and
the mouse (if it exists) must be initialized. Here's some sample code to
accomplish this (just copy this to start a program that will use LangWin):
'===========================================================================
'$DYNAMIC make all arrays dynamic
DEFINT A-Z
' optional: test to see if color EGA or VGA monitor is present.
' see Section 6.16 for techniques to accomplish these tests.
'$INCLUDE: 'LANGWIN.BI' ' TYPE, DECLARE and COMMON definitions for LangWin.
' NOTE: LANGWIN.BI contains all definitions found
' in QB.BI, so include for QB.BI is not needed.
' (similarly for QBX.BI in PDS).
' the following sets the stack at 9000 bytes. if the stack is not large enough
' you'll get some very strange errors. 9000 bytes should be large enough,
' but if you are in doubt, print the value of FRE(-2) to dynamically determine
' available stack space while your program is running.
CLEAR , , 9000 ' set stack to 9000 bytes
' the following is not required by LangWin, but will let you determine
' the screen colors at the time your program was called, and re-establish
' those same colors after your program terminates.
' get attribute from current screen so it can be restored upon exit
OrigAttr = SCREEN(1, 1, 1)' save original attribute from row 1, col 1
' if WIDTH command is used, it must be placed BEFORE call to LangWinInit.
' code in LangWinInit extracts max rows/cols from screen and saves
' in global variables (MaxCols and MaxRows). if WIDTH is used to change screen
' after LangWinInit has determined the screen's row/col dimensions, your
' windows won't work!
WIDTH 80, 25
' these variables MUST be defined BEFORE call to LangWinInit.
' i've used sample values to define these variables.
' you should change these values to meet the requirements of the program
' you are writing, BUT keep these as low as possible
' to conserve memory at run time (see comments below for explanations).
MaxWindows = 5 ' max simultaneous open windows
MaxButtons = 10 ' max number of objects (including non-scroll text) active
' following two variables are new with V2.0:
MaxTextLines = 35 ' maximum number of text lines in any scrollable win
MaxTextWins = 3 ' max windows that can have scrollable text
' must be <= MaxWindows
LOCATE , , 0 ' start with hidden text cursor
' LangWin only supports text mode. You MUST call the SCREEN 0 command BEFORE
' the call to LangWinInit. You can call SCREEN with a video page other than 0
' (i.e., SCREEN 0,,x,x where x is a page number supported by your system).
' Code in LangWinInit will determine which video page you are using and save
' the value in a global variable for use by other LangWin routines. If you
' call SCREEN 0 after LangWinInit and change the original video page, you'll
' get unpredictable results (i.e., LangWin will write to the original video
' page). However, you can use other video pages for functions not associated
' with your LangWin windows; just be sure to set the video page back to the
' original value defined below.
SCREEN 0,,0,0 ' LangWin ONLY supports text mode
' You MUST call the SCREEN command BEFORE LangWinInit
CALL LangWinInit ' initialize (if mouse exists, it will be displayed)
' if you get "subscript out of range" error while
' in LangWinInit, be sure you called QB with /ah.
' then try reducing the value of MaxWindows.
' check the WIDTH command; reduce number of rows.
' display "wallpaper" (background for windows)
IF HaveMouse THEN CALL HideMouseCursor ' first hide mouse pointer
CLS
CALL SetColor(4, 15) ' pick any colors you like (run WINCOLOR for samples)
FOR i = 1 TO MaxRows ' paint the screen
LOCATE i,1
PRINT STRING$(80, 178); ' can try 176, 177, or 178 to get different effects
NEXT
IF HaveMouse THEN CALL ShowMouseCursor ' display the mouse pointer
'====================
' YOUR CODE GOES HERE
'====================
IF HaveMouse THEN HideMouseCursor ' we're done with the mouse
' now restore original screen colors before terminating
bbb = (OrigAttr AND &HF0) \ 16 ' mask & shift to get original background
fff = OrigAttr AND &HF ' mask to get original foreground
PALETTE ' restore original palette
CALL SetColor(fff, bbb) ' restore orig foreground/background
CLS
LOCATE , , 1 ' make text cursor visible
END
_____________________________
Some notes on the above code:
1) LANGWIN.BI also contains the definitions found in QB.BI, so you won't need
a separate "include" for the QB.BI file. LANGWIN.QLB also has the contents
of QB.QLB so you won't need that quick library either. For PDS users,
LANGWIN.BI covers QBX.BI and LANGWINP.QLB covers QBX.QLB.
2) LANGWIN.BI contains all of the COMMON statements for LangWin's global
variables. You should examine these variable names so that you don't
mistakenly re-use the same name in your code (which will cause
unpredictable results).
3) LangWin will support both 80 and 40 columns, as well as 25, 43, and 50
rows (as long as your hardware supports them). You can use the WIDTH
command to set rows and columns; however, the WIDTH command MUST come
before the call to LangWinInit (which tests the screen and sets global
variables with current rows/columns on the screen). WARNING, using 43 or
50 rows will increase the memory requirements for your programs and may
result in "out of memory" errors while developing.
4) PRIOR to calling LangWinInit, you MUST set MaxWindows to the maximum
number of windows that can be opened on the screen at any one time. Keep
this value a small as practical. It is used to DIM several arrays, and a
large value can quickly use up all of the QB environment's free memory (in
this case, you'll get an "out of memory" or perhaps "out of string space"
message while developing). Use small values while developing in the QB
environment. If your production code will need a larger value, set it just
before creating your EXE file. If your program attempts to open more
windows than defined by MaxWindows, the window will not open (you'll get a
negative return code from the routine called to open the window).
5) PRIOR to calling LangWinInit, you MUST set MaxButtons to the maximum
number of buttons, check boxes, input fields, titles, and lines of
non-scrollable text lines that can be active on all visible windows. Like
MaxWindows, this should be kept small during development and increased to a
practical value just before creating your EXE file. A large value of
MaxButtons has less of a memory impact as MaxWindows. Thus, if you have a
memory shortage while developing, reducing MaxWindows will buy back more
storage. If your program exceeds MaxButtons, new objects will not be
created (you'll get a non-zero return code from the routine called to
create the object - if you don't test return codes, you'll notice that
expected objects are missing from some windows). New with V2.1: non-
scrollable lines of text (created with ShowWinText) are saved in LangWin's
data structures (see section 5.0) so they can be re-displayed when a window
is resized. Thus, if you are converting from a previous version of LangWin,
MaxButtons will have to be increased from it's previous value (by the
number of lines of non-scrollable text and titles that can be active on all
visible windows). Space in the data structures is used dynamically. As
windows are opened and objects created, space is used. As windows are
closed, space occupied by objects is freed. So, MaxButtons needs only to be
large enough to store the maximum number of objects, titles, and static
text lines that would be visible on all windows open at any one time (this
includes all windows opened statically BEFORE the WinEvent loop, AND
windows opened dynamically WITHIN the WinEvent loop in response to user
actions).
6) PRIOR to calling LangWinInit, you MUST set MaxTextLines (new with V2.0) to
the maximum number of scrollable text lines in any window. When you open a
window to contain scrollable text (OpenScrollWindow), one of the parameters
passed is a string array containing your scrollable text. LBOUND of this
array MUST be 1; otherwise, OpenScrollWindow will fail with a run-time
error. If the string array with your scrollable text is built dynamically
at run-time, then you must test to determine if the size exceeds
MaxTextLines. If this conditions occurs, then OpenScrollWindow will only
display the first MaxTextLines of text. Your program will have to take
appropriate action to insure that the user will get to see all lines of
text (e.g., give the user the option to see more and if chosen, delete
the first MaxTextLines of text, loop, and open a new window with
scrollable text. Continue until all text has been seen). The contents of
the string array passed to OpenScrollWindow is moved into a global array
called SaveText(i,j) where UBOUND(SaveText,2)=MaxTextLines. Upon returning
from OpenScrollWindow, you can ERASE the string array (since the scrollable
text will have been saved in SaveText). This will help conserve memory.
Of course, if UBOUND(Text$) > MaxTextLines, then you must insure that the
user sees all of the Text$ array before you ERASE it.
7) PRIOR to calling LangWinInit, you MUST set MaxTextWins (new with V2.0) to
the maximum number of simultaneous windows that can have scrollable text.
All scrollable text is saved in SaveText(i,j) where UBOUND(SaveText,1) is
MaxTextWins. The value MUST be less than MaxWindows and (to conserve
memory) should be as low as possible.
8) LangWinInit sets constants, initializes all global variables, DIMs all
arrays, and initializes and displays the mouse (if it exists). The
constants TRUE (-1) and FALSE (0) are defined in LangWinInit and can be
referenced in your code as needed.
9) The global variable HaveMouse is set by LangWinInit and can be tested in
your program to determine if a mouse exists. HaveMouse is set to TRUE if a
mouse exists; else, it is set to FALSE.
10) Before writing anything to the screen, you must hide the mouse pointer (if
a mouse exists). When a mouse pointer is on the screen, and a character is
written to the same position, the character will not be displayed
correctly. If a mouse exists, hide the pointer, write to screen, and then
show the pointer. This technique is used when writing "wallpaper" on the
screen.
11) While not necessary for windows, placing "wallpaper" on the screen for
background provides a nice contrast. ASCII characters 176, 177, or 178 can
be used for a nice effect.
12) Throughout ALL of your code, you MUST use the SetColor routine instead of
the COLOR command to change color attributes. The parameters are the same
(foreground and background attributes) as the COLOR command. LangWin uses
BIOS interrupt 10h, function 10h, sub-function 03h to disable blinking
colors. This allows 16 attribute numbers for window (background) colors
(rather than just 8). However, with blinking disabled, foreground and
background attribute numbers MUST be translated before calling BASIC's
COLOR command. SetColor does this translation and issues the COLOR command
with the proper values. If BASIC's COLOR command is called without this
translation, you will get unexpected colors.
13) You MUST use the SCREEN 0 command BEFORE calling LangWinInit (V2.3). You
can use any video page supported by your system. LangWinInit saves the
video page you select in a global variable which is used by other routines
to read/write data in the video buffer. Thus, once you select a video page
in the initial SCREEN command (which must be BEFORE the call to
LangWinInit), you should not change the video page in a subsequent
call to SCREEN. In general, there's no need to have another call to SCREEN.
One exception: for functions not associated with LangWin, you could use
SCREEN to change to a new page, display information on the screen, then
use SCREEN to change back to the original page number used in the
initial SCREEN command. For example, you could use LangWin to display
a scrollable list of filenames in one page. When a file name is clicked,
you could use SCREEN to change pages, SHELL to DOS and call a viewer to
display the selected file. Upon return from the SHELL, use SCREEN to
change back to the original page (containing the screooable list). In this
case, be sure to test for a mouse and hide it before SHELLing and restore
it afterwards.
14) I used the CLEAR command to initialize the stack at 9000 bytes. To save
memory and improve performance, LangWin is not compiled with the run-time
debug option. Thus, you won't get an explicit warning if you exceed the
stack space. Instead, you will get "strange" errors. In one case, the
errors did not show up while developing in the QB environment. However,
when testing with compiled code, strange things began to happen when the
compiled program terminated. Characters no longer appeared on the screen,
my machine locked up, or re-booted itself. If you suspect that you may be
exceeding the stack space (usually caused by many recursive calls to
subroutines/functions, or just many subroutines/functions with many
parameters), print the value of FRE(-2) dynamically in your program
to determine stack space capacity and increase as necessary.
If FRE(-2) shows plenty of stack space free, you can reduce the size of
the stack or even omit the CLEAR command entirely.
3.2 Some Simple Windows
-----------------------
Now, let's get on with creating some windows. LangWin requires a technique
called event-driven programming. (For those with Version 1.x of LangWin, the
event loop technique has been changed as of V2.0; you'll need to change your
program's logic).
In general, for event-driven programming you must write code segments to handle
every possible event in every possible window, whether or not the window has
been opened yet (i.e., you must not only consider static windows that you open
when the program initializes, such as a main menu, but also any dynamic window
that will be opened "on the fly" based upon what object the user clicks, such
as an error window or sub-menu window). An event is defined as any
button/icon/text click. Your program consists of one main loop (new with V2.0;
previous versions required other loops), where you first call a special LangWin
routine (WinEvent) that will process all events. When an event occurs, the
special routine will return control to your program which must then determine
the exact window and event that occured, and execute a code segment dedicated
to handling that combination of window/event. In event-driven programming, the
event "drives" the code segment dedicated to processing that event. Your
program essentailly waits for an event (i.e., waits for control to be returned
from WinEvent), drives the corresponding code, and continues looping (until
some event occurs that signals termination).
A high level view of your program's logic would be:
open static window(s); save window numbers in unique variables
create buttons, input fields, click boxes as needed; save handles in
unique variables
do while any window is open
wait for an event to occur in any window (call WinEvent)
determine which window had focus
determine which event occurred in the window that had focus
process the window/event combination (could open dynamic windows)
loop
As you can see, once static window(s) have been opened, you will loop: waiting
for an event, processing the event, and continuing the wait/process cycle as
long as any window remains open. Each possible window/event combination will
have a separate code segment dedicated to processing that event. Among the
actions that could be taken within the code segment, a dynamic window with its
own objects (buttons, click boxes, input fields, etc.) could be opened. If this
is done, then your program would also need a code segment to handle events in
this (and all other possible) dynamic window(s).
The LangWin routines needed to accomplish the above tasks are:
(these are just a sub-set of all routines in LangWin, see WINHELP.BAS
for complete documentation):
BlankWin - opens a plain window; returns window number
OpenScrollWindow - opens a window with scrollable text; returns number
MakePushButton - creates a push button; returns its handle value
MakeCheckBox - creates a check box; returns its handle value
MakeInputField - creates an input field; returns its handle value
WinEvent - processes all GUI activity within all open windows.
returns a value that identifies the number of
the window with focus (see Section 5.7 for more
information on window numbers), and returns a
parameter that defines the event that occurred
(1=close; 2=text line selected; 3=button selected).
CloseWindow - close the current window
ShowWinText - places static (non-scrollable) text into current
window
See Section 6.7 for additional details on event-driven programming techniques.
-----------------------
4.0 USING LANGWIN'S GUI
-----------------------
Before describing LangWin's data structures and providing more advanced
techniques for doing windows, a brief description of how to use the GUI
created by LangWin will be presented.
Windows created by LangWin have the following standard GUI features:
4.1 Close Icon
--------------
The upper left corner of each window can optionally have a close icon (this is
determined by the CloseIcon parameter in the BlankWin and OpenScrollWindow
routines, see WINHELP.BAS). Double click on this icon and WinEvent will return
an action code of 1 to your program. Regardless of whether a close icon is
displayed, hitting the ESC key will return an action code of 1 to your program
(if you are in an input field, ESC will exit the input field; hit ESC again if
you want to close the window. See Input Fields below for details). You can
choose to ignore the close event in your program, and instead require a
specific button click to effect the window being closed.
4.2 Moving, Resizing, and Minimizing the Active Window
------------------------------------------------------
4.2.1 Moving
------------
Windows can be moved to any position on the screen (as long as the window is
marked as movable). Using the left mouse button, click and hold the close icon
(if there is no close icon, click and hold the upper left corner of the
window). After a moment (in this case, a "moment" equals approximately 2/9
second), the mouse pointer will change to a pair of "corner pointers": one in
the upper left corner of the window and one in the lower right. (If the mouse
pointer does not turn into "corner pointers", then the window is not movable -
see description of OpenScrollWindow and BlankWin for details on how to mark a
window as unmovable). While holding the mouse button down, drag the pair of
"corner pointers" to a new position and release the mouse button. Don't forget
that if the window has a shadow, the new position must have room for the
shadow (the "corner pointers" only show the extent of the window, not it's
shadow which takes two columns on the right and one row below the window).
When moving a window, the mouse cursor is limited such that you won't be able
to move the window off of the screen (if the window has a shadow, the cursor
limit will leave room for it).
See the WinColor parameter in BlankWin and OpenScrollWindow for details on how
to mark a window as unmovable. See the BorderColor parameter (in the same two
routines) for details on making a window shadowless.
4.2.2 Resizing
--------------
Using the RIGHT mouse button, click and hold any of the window's corners. After
a moment (2/9 sec) the mouse pointer will change into a "corner pointer". (If
the mouse pointer does not change, then the window is not resizable - see
OpenScrollWindow and BlankWindow for details on how to mark a window as not
resizable). Drag the corner pointer to a new position and release. The window
will be resized appropriately. LangWin imposes certain limits when shrinking
windows. If the window has a scrollable text area, the window cannot be made so
small that the text area would disappear (the smallest that the text area can
be is one character). In this case, the resulting size of the window containing
scrollable text will depend upon the size of the borders (within the window)
around the scrollable text area (which can be anywhere within a window - see
OpenScrollWindow). If the window does not have scrollable text, then the
smallest it can be is three characters by three characters (including the
characters that make up the border).
When a window is expanded or contracted, the text area (if present) and all
buttons, input fields, check boxes, titles, and plain text (non-scrollable)
will be moved proportionally. When a window is contracted, these objects will
be closer together and may appear "squished". If the window is made small
enough, some of these objects will DISAPPEAR (because they will no longer be in
the visible area of the window). Making the window larger will cause these
objects to re-appear. However, some objects WILL disappear altogether when a
window is resized. These include horizontal lines (see MakeHorizLine), vertical
lines (MakeVertLine), and boxes (MakeBox) - also see Section 6.1. If this
presents a problem (i.e., you NEED to have these objects in your window, and
having them disappear when your end user resizes the window is not acceptable,
then mark the window as not resizable when it's opened).
See the BorderColor parameter in BlankWin and OpenScrollWindow for details on
how to mark a window as not sizable.
HINT: when a window is resized, it's objects are redrawn (at new positions) in
the same order in which they were created. If the window gets too small, some
adjacent objects (in the original window) may be redrawn in the same space (in
the smaller window) and overlay one another. In this case, the last object
redrawn will appear "on top" of others (and may even completely hide others
that are "beneath" it). Thus, for adjacent objects in the original window, the
object want to appear "on top" of others (when a window is made smaller) should
be defined last. For example, as the window is made smaller, non-scrollable
text (see ShowWinText) in the window's second row and the window's title (see
ShowTitle) in first row might both be placed on the first row of the window. If
you want the title to appear "on top" of the non-scrollable text in this case,
then place the CALL ShowTitle after all CALL ShowWinText (for text in the given
window).
HINT: a window with scrollable text can be opened without a scroll bar (for
example, when items in a scrollable list are used to select sub-menus, and you
know that the entire list will fit into the original window, then you might
choose to specify no scroll bar for the window). If you shrink this window to
the point where all of the scrollable text will not fit, then some scrollable
text (i.e., your menu items) will not be displayed. In this case, a scroll bar
will NOT be automatically included when the window is shrunk (because the
original specification to omit a scroll bar will take precedence). If you must
open a window with scrollable text and specify that that it not have a scroll
bar, then I'd recommend making the original window not sizable (see BorderColor
parameter in OpenScrollWindow routine).
HINT: if a window is marked as unmovable, it cannot be resized (regardless of
the state of the resize flag).
4.2.3 Minimizing
----------------
Place the mouse cursor ANYWHERE in the window, then click and hold BOTH left
and right buttons. After a moment (2/9 sec), the window will be contracted to
its minimal size (by simulating the process of dragging the lower right corner
to the upper left as far as possible - see previous section for a discussion on
minimal size). Repeating this process on a window that has been minimized will
(in most cases) expand the window to its original size (by simulating the the
process of dragging the lower right corner back to the original position).
Both resized and minimized windows can be moved (left click on upper left
corner and drag). If a minimized window is moved closer to the bottom row or
right edge of the screen, then clicking with both buttons to expand back to
original size would result in the window extending beyond the screen's
boundaries. In this case, the window will be expanded as large as possible (but
may indeed be smaller than its original dimensions).
See the BorderColor parameter in BlankWin and OpenScrollWindow for details on
how to mark a window as not sizable (which means it cannot be minimized).
HINT: if the window's size has been changed (by dragging a corner with the
right button), and you want to return the window to its original size, first
click with both buttons to minimize, then click with both buttons again to
expand back to original size. Of course, you could also drag a corner with the
right button to resize back to original dimensions (unless you don't remember
what original window looked like).
HINT: here's a technique I've found for clicking both buttons to minimize.
Place the mouse pointer on the window to be minimized (but not on a corner).
Click the right button, then click the left button. The point is that the right
button should be clicked first (so that just in case you happen to be on a
button or some other object, no action will be taken), then the left button is
clicked. You don't have to actually click both simultaneously; they both just
have to be down. I found that trying to click both simultaneously sometimes
actually results in the left button being clicked first. In this case, if the
pointer happens to be on and object, that object will be selected. Yes, I could
have used another icon on the window's border to implement minimizing; I just
liked the idea of being to minimize with the mouse cursor anywhere on the
window in question. My apologies to those who dislike the right mouse button
(but as long as it's there, I thought I'd use it).
HINT: if a window is marked as unmovable, it cannot be resized (regardless of
the state of the resize flag), and thus it cannot be minimized.
4.3 Focus
---------
Before discussing buttons, check boxes, input fields, and scrollable text, the
concept of focus must be reviewed. Focus has taken on additional meaning with
V2.0 and later. A window can have focus (i.e., it's the current window), and an
object within the current window can have focus.
When WinEvent returns control to your program, the value of the WinEvent
function corresponds to the number of the window that had focus when the event
occurred. The value of WinEvent's parameter is a code for the type of action
that occurred in the window with focus. You will need to determine which line
of text or button caused that event (i.e., had focus when the event occurred).
Clicking anywhere on a window gives it focus. Clicking on a window's object
(i.e., button, check box, input field, or scrollable text) gives the owning
window and the corresponding object focus (input fields will display a cursor
when they have focus, other objects will be displayed in reverse video). Note
that if scrollable text exists, one line ALWAYS has focus. If buttons, check
boxes, and input fields exist, one of them usually has focus (but it is
possible for none of the buttons, check boxes, or input fields to have focus,
see Changing Focus below). Thus, it is possible for BOTH a line of text AND a
button to have focus when WinEvent returns to your program. See Section 6.6 for
details on how to distinguish between the line of text with focus and the
button with focus when WinEvent returns control to your program.
4.4 Changing Focus
------------------
With LangWin 2.0, you can mouse to any visible window and select an object.
Thus, you'll need to understand the difference between the current window with
focus and the current object in a window with focus. There are actually two
kinds of focus: "window focus" - the current window; and "object focus" - the
current object in a window. Clicking on a window gives it "window focus".
Clicking on an object in a window gives "window focus" to the selected window
and "object focus" to the selected object. The following two sub-sections will
help to clear up these concepts.
4.4.1 Window Focus
------------------
Only one window can have focus. If your program creates a window (with BlankWin
or OpenScrollWindow), then it has window focus. Until that focus is changed,
any subsequent objects created with "make" routines will be associated with
that window. Window focus can be changed by clicking anywhere on any visible
window. That window will be moved to the "top" of the stack of overlapping
windows and given focus (in addition, if an object in that window was clicked,
it will be given object focus - see next section). Pressing CTRL and PgUp will
also change the window in focus (the "bottom" window on the screen is moved to
the "top" and given focus). Repeated use of CTRL-PgUp will cycle through all
visible windows. If the windows displayed are overlapping, you'll easily be
able to tell which window has focus (it will appear on "top" of the stack). If
the windows do not overlap, there won't be a visible difference between the
window with focus and all others. If you use the keyboard to control the GUI
(see Section 4.10), then keyboard commands (such as TAB, ENTER, etc) take
effect only in the window with focus.
4.4.2 Object Focus
------------------
Every object in a window can also be given focus. Typically, just clicking on
the object will give it focus and it will be displayed in reverse video (when
input fields have focus, a text cursor appears in the field). While it is
possible for no button or check box to have focus, if there is scrollable text
in the window, then one line will ALWAYS have focus (one exception: null lines
in scrollable text can have focus, but they will be invisible and appear as if
no scrollable text has focus). There are several ways to change focus. Clicking
on any object will give it focus. If a line of scrollable text is clicked, then
it is given focus (and focus is removed from any button or check box previously
having focus). If a button, check box, or input field is clicked, then it
obtains focus (and the text focus is unchanged). Thus, if both scrollable text
and other objects exist in a window, there will always be one line of text with
focus, and there may or may not also be another object with focus.
In addition to clicking on an object to give it focus, the TAB and Shift-TAB
keys can be used to change the focus of buttons, check boxes, or input fields
in the current window (text focus is not affected by TAB). Hitting TAB will
move focus to the next button, check box, or input field (if scrollable text
exists, one line ALWAYS has focus, and this focus remains unchanged by the TAB
keys. Text focus is only changed by mouse or scrolling). Shift-TAB will move
the focus to the previous button, check box, or input field. After cycling
through all buttons, check boxes, and input fields, the focus will disappear
(except for the line of scrollable text with focus). Hitting TAB or Shift-TAB
will cause the focus to reappear on the next/previous button, check box, or
input field.
Unless an input field is being actively edited (see Input Field), the
left/right arrow keys will function like TAB/Shift-TAB for changing focus. The
up/down arrows change text focus, scrolling the list as appropriate.
If scrollable text does not exist, then the up/down arrows will also function
like TAB/Shift-TAB for changing focus.
When you hit ENTER, the object with focus is selected (just as if it were
clicked with a mouse). If BOTH a line of scrollable text AND an object
(i.e., push button or check box) has focus, then an action code of 3 will
be returned from WinEvent (i.e., the object takes precedence). In this case,
your code can check WinParms(CurWinPtr,16) to get the handle of the button
pushed and WinParms(CurWinPtr,18) and WinParms(CurWinPtr,15) to get indices
in SaveText that correspond to the line of text currently in focus (see Section
5.8).
4.5 Scrolling Text
------------------
The right side of each window can optionally show a scroll bar, slider, and
up/down arrows. These will not be present if a plain window was opened
(BlankWin), or if you specifically open a window with scrollable text, but
specify no scroll bar (see BorderType parameter in OpenScrollWindow). If the
scrollable text array will completely fit into the defined text area of the
window (i.e., nothing to scroll), then the slider will not be displayed (scroll
bar and arrows will still be visible). If a scroll bar exists, then WinEvent
will handle all scrolling for you. Scrolling will change the text line that has
focus (shown in reverse video). The user can click on the up/down arrows to
scroll one line at a time. Click on the scroll bar (not the slider) and the
text will page up/down (if the click was below the slider, page down; above the
slider, page up). Drag the slider up/down, and the text will scroll to the
correct relative new position of the slider when the mouse button is released
(usual GUI stuff).
The keyboard can also be used to scroll. Up/down arrows for single lines.
Home/End keys for start/end of text, and PgUp/PgDown for paging.
If the scrollable text has null lines, when one of these is given focus
(clicking, arrows, scrolling, etc.), it will still appear as null (i.e.,
invisible). This will make the scrollable text focus seem to disappear when
selecting a null line. This could be confusing to the end user, and unless you
have a specific use for it, I'd recommend that your scrollable text not include
null lines. When OpenScrollWindow creates a list of scrollable text, all null
entries that exist at the end of the text array are eliminated (null lines
within the text array are not eliminated).
4.6 Selecting Scrollable Text
-----------------------------
The OpenScrollWindow takes a parameter that defines an array of scrollable
text. This text is displayed in the window, and as previously described, can
be scrolled up or down. Single clicking on a line of text will give it focus.
Double-clicking on a line of text will give it focus, AND will return control
from WinEvent with an action code of 2 (value of the WinEvent function will be
the window's number where the text was clicked).
4.7 Push Buttons
----------------
The MakePushButton routine is used to create push buttons in the current window
(it returns a unique handle number associated with the button created). These
buttons can optionally have drop shadows. Clicking on any push button will give
it focus, AND will return control from WinEvent with an action code of 3 (value
of the WinEvent function will be the window's number where the button was
clicked). Depending upon the logic of your program, you can deactivate/activate
individual push buttons. Deactivating the button will cause its text to clear
(the button itself, and any shadow, is still visible, just its text is clear).
Any subsequent clicks on a deactivated button will NOT produce any events.
Activating the button will cause its text to be visible and click events to be
recognizes. A button can be used to open a child window. If you do not want the
end user to be able to click the button again while the child window remains
open, then use the deactivate function. When the child window is closed,
activate the button.
4.8 Check Boxes
---------------
The MakeCheckBox routine is used to create check boxes in the current window
boxes are toggle switches used to select (un-select) user defined options.
Clicking on a check box will give it focus and change its state (from up to
down, or down to up). There are no check box actions/events that would cause
WinEvent to return control to your program; thus, there are no WinEvent action
codes associated with check boxes. However, after control is returned to your
program (for some other event), you can examine the state of all check boxes in
the current window. WinEvent's return value will tell you the current window,
and the code you write to handle that window's events should test each check
box created in the window (by referencing the specific variables you used to
save the handles of all boxes in that window). Depending upon the state of the
check boxes in the current window, your code can take any necessary action (see
DATA STRUCTURES and ADVANCED WINDOWS below).
4.9 Input Fields
----------------
The MakeInputField routine is used to create input fields in the current window
(it returns a unique handle number associated with the field created). An
input field is an editable area that can be used for data entry. Tabbing to, or
clicking on, an input field will give it focus (cursor will be visible) and
make it available for data entry and/or editing. Input fields can be re-
selected at any time to make changes/corrections as necessary. There are no
actions/events from an input field that would cause WinEvent to return control
to your program; thus, there are no WinEvent return codes associated with
input fields. However, after control is returned to your program (for some
other event), you can examine the contents of all input fields in the current
window to see if they have been updated. WinEvent's return value will tell you
the current window, and the code you write to process that window's events
could test the current contents of each input field created in the window (by
referencing data associated with each input field's unique handle). Your code
would have to "remember" the previous contents to determine if the input field
had been changed. Depending upon the current contents of the input fields in
the current window, your code would take any necessary action (see DATA
STRUCTURES and ADVANCED WINDOWS below). The following keys are available while
in an input field:
ESC - exit input field (useful if you have several input
fields, but want your mouse pointer back immediately)
current window retains focus
Ctrl-PgUp - exit the input field (next WINDOW given focus)
(new with V2.0)
ENTER - exit input field (next object given focus)
TAB - exit input field (next object given focus)
DOWN Arrow - exit input field (next object given focus)
Shift-TAB - exit input field (previous object given focus)
UP Arrow - exit input field (previous object given focus)
(the above 5 keys are useful if you have several input
fields and want to go to next/previous field immediately
after completing the current field - data entry)
Ctrl-c - clear entire contents of the input field
Ctrl-y - clear entire contents of the input field
Ctrl-END - clear input field from cursor to end
INSERT, BACKSPACE, LEFT/RIGHT Arrows, DELETE, HOME, END
(the above keys should be self explanatory)
While in data entry mode (within an input field), the mouse remains active. In
addition to the Arrow keys, you can use the mouse to position the text cusror.
In addition to the ESC key, you can use the mouse to exit an input field (just
click anywhere outside the input field).
An input field can easily be used for password entry. When defining the input
field (via MakeInputField), use a negative value for its length (new with
V2.2). This will force all text entered into the field to be displayed with
the * character. Another technique (available in previous releases) for
entering passwords would be to use the same value for both the foreground and
background colors of the input field. In this case, nothing will be displayed
when text is entered. For both techniques, the actual text entered (i.e., the
password) is saved in ButtonsText(handle) - just as for any other input field -
and could be used to validate the password. See DATA STRUCTURES for a
description of the contents of ButtonsText.
4.10 No mouse
-------------
For those without a mouse (or those writing code that might run on systems
without a mouse), LangWin's GUI functions can be selected via the keyboard. The
Changing Focus Section (above) already described how to use the keyboard to
change focus. To select the current object with focus, just hit ENTER.
If a push button has focus, hitting ENTER will return control from WinEvent
with a value of 3. (Note that even though a line of scrollable text ALWAYS has
text focus, if a button has focus, hitting ENTER will return from WinEvent with
a code of 3. In this case, if you need to know which line of text also had
focus, then this can be done - see DATA STRUCTURES and ADVANCED WINDOWS for
additional details).
If a check box has focus, hitting ENTER will toggle the check box, but an event
code will NOT be generated and control will remain within WinEvent.
If an input field has focus, hitting ENTER will exit the input field and move
focus to the next object (in current window). An event code will NOT be
generated and control will remain within WinEvent.
If ONLY a line of scrollable text has focus (i.e., no button, check box, or
input field has focus), hitting ENTER will return control from WinEvent with am
action code of 2. By hitting TAB, you will cycle the focus through all buttons,
check boxes, and input fields, and eventually get to a state where the ONLY
object with focus is a line of scrollable text (if no scrollable text exists,
then focus disappears).
----------------------------------------
5.0 DATA STRUCTURES AND GLOBAL VARIABLES
----------------------------------------
The following subsections describe LangWin's data structures and global
variables (some of which have been added/changed with LangWin 2.0). You will
need this information for the advanced window techniques presented in the
Section 6.0. The global variables that are user definable could either be hard
coded in your program or read from an INI file (so your end user can change
them to meet system requirements and/or constraints).
5.1 MaxWindows
--------------
This global variable defines the maximum number of open windows that can appear
on the screen at one time. If you try to open more windows than MaxWindows, the
BlankWin or OpenScrollWindow routines will fail and return an error code.
MaxWindows MUST be defined by you PRIOR to calling LangWinInit and SHOULD NOT
BE CHANGED (see Section 3.1 for additional details).
5.2 MaxButtons
--------------
This global variable defines the maximum number of push buttons, check boxes,
input fields, window titles, and lines of non-scrollable text that can be
created on all possible open windows. If you attempt to create more objects,
the corresponding "make" routine will return an error code. MaxButtons MUST be
defined by you PRIOR to calling LangWinInit and SHOULD NOT BE CHANGED (see
Section 3.1 for additional details).
New with V2.1: non-scrollable lines of text (created with ShowWinText) are
saved in LangWin's data structures (see section 5.0) so they can be re-
displayed when a window is resized. Thus, if you are converting from a previous
version of LangWin, MaxButtons will have to be increased from it's previous
value (by the number of lines of non-scrollable text and titles that can be
active on all visible windows). Space in the data structures is used
dynamically. As windows are opened and objects created, space is used. As
windows are closed, space occupied by objects is freed. So, MaxButtons needs
only to be large enough to store the maximum number of objects, titles, and
static text lines that would be visible on all windows open at any one time
(this includes all windows opened statically BEFORE the WinEvent loop, AND
windows opened dynamically WITHIN the WinEvent loop in response to user
actions).
5.3 MaxTextLines
----------------
This global variable defines the largest number of scrollable text lines that
can appear in any window. If your program will open two windows, one with 25
lines of scrollable text and another with 50 lines, then (in this case)
MaxTextLines would have to be set to 50. MaxTextLines MUST be defined by you
PRIOR to calling LangWinInit and SHOULD NOT BE CHANGED (see Section 3.1 for
additional details). New with 2.0.
If the number of scrollable text lines to be displayed in a window is not
known during development, but will be determined dynamically at run-time, then
you must take a best guess at a reasonable value for MaxTextLines.
OpenScrollWindow will compare the UBOUND of the string array passed with
MaxTextLines. If UBOUND(Text$) > MaxTextLines, then OpenScrollWindow will
only display the first MaxTextLines of the array. In this case, the last
line displayed will be set to the string: "(Incomplete List)"
so your user will be aware that some data is missing. Your program should
detect this condition (by comparing UBOUND(Text$) to MaxTextLines) and take
appropriate action. For example, your could offer the user an option to
see the missing text, and if selected, delete the first MaxTextLines from the
Text$ array, loop, and open a new scrollable text window. Continue until
all text has been displayed. Alternatively, you could just split the
original Text$ array into smaller arrays (size <= MaxTextLines) and open
a scrollable window for each. I'll leave the details to you!
5.4 MaxTextWins
---------------
This global parameter defines the maximum number of windows that can be open
with scrollable text. MaxTextWins MUST be <= MaxWindows (makes sense; can't
have more text windows than windows of any kind, right!). MaxTextWins is
used to dimension the global array SaveText which will hold all scrollable text
in all open windows (see Section 5.8 for more details on SaveText array).
MaxTextWins MUST be defined by you PRIOR to calling LangWinInit and SHOULD
NOT BE CHANGED (see Section 3.1 for additional details on MaxTextWins).
New with V2.0.
5.5 AnyWinOpen
--------------
With LangWin 2.0, this global variable is created, defined, and changed by
LangWin's routines. It will be TRUE when any window is open and FALSE when all
windows have been closed (DO NOT CHANGE THIS VARIABLE). You can use it to
control the exit from your loop that checks WinEvent as follows:
DO WHILE AnyWinOpen
wnum=WinEvent(action)
process the action in window # wnum
LOOP
5.6 CurWinPtr
-------------
When a window is opened (via BlankWin or OpenScrollWindow), its parameters are
stored in an available "slot" of the WinParms array (see Section 5.9). The
"slot" in WinParms is called the window's handle. The handle of the current
window with focus can always be found in the global variable CurWinPtr.
CurWinPtr is created, defined, and updated by LangWin's routines (DO NOT DEFINE
OR CHANGE IT).
5.7 Window numbers vs window handles
------------------------------------
With LangWin 2.0, when a window is opened (via BlankWin or OpenScrollWindow),
it is given a unique window number (i.e., the value returned by the function
used to open the window). The window number should be saved in a unique
variable name. The window's parameters are stored in the WinParms array. The
"slot" in WinParms where the window's data is saved is called its handle.
Window numbers are unique; window handles are not unique. Window handles are
re-used after a window is closed (i.e., the "slot" in WinParms will be re-used
to hold a new window's parameters).
The window's handle cannot be used to uniquely determine what to do when
control returns from WinEvent (i.e., after an event occurred in a window). The
window's number, however, is a unique sequential value which is never re-used.
The number of the window where an event occurred must be used to uniquely
determine how to process events in that window. For example, handle number 3
might point to data for window number 6 at one point, and after that window is
closed and another is opened, handle number 3 could then point to data for
window number 7. If the window's handle (3) were used to select code to process
the event, you would not know exactly what to do because either window 6 or 7
could have caused the event. Using the window's number, however, will tell you
exactly which window caused the action, and thus you can select the proper code
to handle events for that window (each window will have a different set of code
to handle its events, you'll also have to determine which event occurred in the
given window). This is probably the only time you'll need the window's number
(i.e., to select the proper code to handle a window's events). Within your code
to process events for a window, use CurWinPtr to index the WinParms array
to get data for the event that occurred. CurWinPtr contains the handle of the
current window with focus (which is the window that contained the event that
caused control to be returned from WinEvent). Section 3.2 contains pseudo code
that demonstrates this technique (called event-driven programming). WinNum is a
cross reference array containing window numbers. WinNum(i) contains the window
number that corresponds to handle i - see Section 5.14 for details on WinNum.
5.8 SaveText
------------
This structure is DIMed as follows:
SaveText(1 to MaxTextWins, 1 to MaxTextLines) AS STRING
It is used to hold all scrollable text in all currently open windows. The
first dimension defines "slots" for the set of scrollable text in a given
window. The second dimension defines specific lines of text in that window.
OpenScrollWindow takes the string array passed with scrollable text and copies
it into an open slot in SaveText. It saves the "slot" value in
WinParms(CurWinPtr,18) - see Section 5.9 for details on WinParms().
Upon returning from WinEvent, if action=2 (i.e., text line selected), then
SaveText(i,j) will be the line of text with focus; where:
i=WinParms(CurWinPtr,18) and j=WinParms(CurWinPtr,15)
SaveText is new with V2.0.
5.9 WinParms
------------
This structure is DIMed as follows: WinParms(1 to MaxWindows, 1 to 19) The
first dimension is called the window's "handle". This is the "slot" where data
for a given window is saved when it's opened. CurWinPtr (see Section 5.6)
always contains the handle of the window with focus.
Each open window has the following 22 entries defined:
1 --- starting row of window (relative to screen)
2 --- starting column of window (relative to screen)
3 --- ending row of window (relative to screen)
4 --- ending column of window (relative to screen)
5 --- color attribute of window's background
6 --- color attribute of window's border
7 --- border type: (neg value will prevent scroll bar from being used)
1 = single line, 2 = double line
-1 = single line, -2 = double line
8 --- color attribute of foreground text in window
* 9 --- index of scrollable text's array entry currently at top of win
10 --- starting row of scrollable text area (relative to window)
11 --- starting column of scrollable text area (relative to window)
12 --- ending row of scrollable text area (relative to window)
13 --- ending column of scrollable text area (relative to window)
* 14 --- absolute screen row of scrollable text line that currently has focus
* 15 --- 2nd index in SaveText array: text line with focus (-1 if no text)
* 16 --- handle of button with current focus (-1 if no button has focus)
17 --- last row of SaveText array with non-null value
18 --- 1st index in SaveText: all of window's text (-1 if no text)
19 --- mode of window (1=modeless; 2=modal; 3=immediate close; 4=wallpaper)
(negative values imply window is shadowless)
20 --- # rows in original window (used for resize)
21 --- FLAGS
01h - movable (1=movable; 0=unmovable)
02h - sizable (1=sizable; 0=not sizable)
* 04h - window minimized status (1=minimized; 0=not minimized).
22 --- # columns in original window (used for resize)
Values marked with * are dynamic and will change as the active window is
manipulated within the WinEvent function. All other values remain unchanged
after window is opened (unless window is moved, in which case row/col values
are translated based upon new location of window). If not used (i.e., no
scrollable text, buttons, etc. in the window), then entries 9-22 are
initialized to -1.
Entries 17-21 were new with V2.0.
Entries 20-22 were changed in V2.1.
Examples: If WinEvent returns an action code of 3 (signifying a button
selection event), then WinParms(CurWinPtr,16) contains the handle of the button
that was clicked. If you code; han=WinParms(CurWinPtr,16) in the routine that
processes a button click, then ButtonsData(han,i) contain information on the
corresponding button (see Section 5.12) and ButtonsText(han) contain the actual
text in the button (see Section 5.11).
If WinEvent returns a value of 2 (scrollable text line selected), then
SaveText(i,j) contains the line of text that was selected (i.e., has focus);
where: i=WinParms(CurWinPtr,18) and j=WinParms(CurWinPtr,15)
Note that if the unmovable flag is set, then the window also cannot be resized
(regardless of the state of the sizable flag).
5.10 button handles
-------------------
When a push button, check box, input field or static text is created with
LangWin's corresponding "make" or "show" routine, its data is stored in a
"slot" of the ButtonsText and ButtonsData structures (described below). This
"slot" is the index of the object's entry in these structures and is referred
to as its "handle". The "make" routines return a handle value which should be
saved in a unique variable name for later reference.
5.11 ButtonsText
----------------
This structure is DIMed as follows: ButtonsText(1 to MaxButtons) AS STRING
If Han is the handle returned by the corresponding "make" or "show" routines,
then for each of the following objects, ButtonsText(Han) contains
(see ButtonsData(i,8) in next section for button type):
BUTTON TYPE CONTENTS OF ButtonsText(Han)
----------- ----------------------------
Push Button: The push button's text (created with MakePushButton).
Check Box: The symbol currently displayed in the check box (created with
MakeCheckBox). By checking the state of the check box symbol's
shadow (see Section 5.12), you can determine if the box was
selected or not.
Input Field: The current contents of the input field (created with
MakeInputField). Input fields can either be created with initial
text or a null string and can be modified by the user. By checking
the contents of ButtonsText(handle) you can determine if the user
made any changes (your code will have to "remember" the previous
contents to determine if a change was made). If the input field is
created (MakeInputField) with a negative value for length, then
it can be used for password entry. All text entered will be
displayed with the * character, but the actual password will be
saved in ButtonsText(handle).
Static Text: A line of static text placed into the window with the
ShowWinText routine or a window title created with the
ShowTitle routine. Each line of static text and each title
occupies a separate entry in ButtonsText (new with V2.1).
5.12 ButtonsData
----------------
This structure is DIMed as follows: ButtonsData(1 to MaxButtons, 1 to 10)
All values are initialized to -1.
Each push button, check box, input field, or static text has the following 8
entries defined:
1 --- index to WinParms entry (i.e., the handle) of the window containing
this button.
(0 if this entry is unused).
(<0 if this object is not visible,
i.e., window was resized smaller and object no longer fits).
2 --- button's current absolute row (relative to entire screen).
3 --- button's current absolute column (relative to entire screen).
4 --- length of button (in characters).
(<0 if button is visible but inactive; see DeactivateButton in 6.1)
5 --- foreground color attribute.
6 --- background color attribute.
7 --- current shadow state (0 = no shadow; 1 = shadow).
[can be used to determine if check box is up (1) or down (0)]
8 --- button type: 1 = push button, 2 = input field, 3 = check box
4 = static text.
9 --- # rows in original window (used for resizing).
10 --- # cols in original window (used for resizing).
For window titles, entry 8 will be a 4, and entries 2, 3, 4, 9, and 10 will be
a -1.
Entries 2, 3, and 7 are dynamic and can change as button is moved or
(in the case of a check box) selected.
Examples: If you defined a check box in the current window, and saved its
handle value (returned by MakeCheckBox) in C1, then you can test the value of
ButtonsData(C1,7) after WinEvent returns control (regardless of the event) to
determine the state of the check box. If ButtonsData(C1,7)=0, then the check
box has no shadow and it is down. If ButtonsData(C1,7)=1, then the check box
has a shadow, and it is up.
5.13 UserHotKeys
----------------
WinEvent sets 3 standard action codes: 1 = close; 2 = text line selected; 3 =
button selected. The UserHotKeys data structure is used to define additional
GLOBAL hot keys (i.e., available from ALL open windows) and corresponding
action codes. Under most circumstances, UserHotKeys should be defined once
BEFORE the main WinEvent loop.
Each user defined hot key has the following 2 entries in UserHotKeys:
1 --- ASCII code (decimal) corresponding to the hot key. This is the code
that is returned by INKEY$ when the key is pressed. For 2-byte key
codes (for example Alt-z returns two bytes: 0 and 44), use the
negative value of the second byte (which would be -44 in the above
example). For 1-byte codes (i.e., Ctrl-z returns a 26, Shift-z
returns a 90, and z returns a 122), use the positive value of the
ASCII code.
2 --- WinEvent action code corresponding to the above key. WARNING:
action code must NOT be 1, 2, or 3. These are reserved for WinEvent
(if you choose action codes 1, 2, or 3, your hot key will not be
recognized).
LangWin initializes UserHotKeys with 0 entries as follows:
REDIM UserHotKeys(0,1 to 2)
If you want to define N specific GLOBAL hot keys, then you must first REDIM
the UserHotKeys array for N entries (i.e., REDIM UserHotKeys(0 to N, 1 to 2),
and then define each hot key and its corresponding WinEvent action code in the
UserHotKeys array (the 0th entry is required, but MUST be left empty).
For example, to define 3 hot keys (q, Ctrl-v, and Alt-m) with corresponding
WinEvent action codes (4, 5, 6), the following code would be needed:
REDIM UserHotKeys(0 TO 3, 1 TO 2)
' first hot key's definition
UserHotKeys(1, 1) = 113 ' letter "q"
UserHotKeys(1, 2) = 4 ' WinEvent action=4
' second hot key's definition
UserHotKeys(2, 1) = 22 ' ctrl-v
UserHotKeys(2, 2) = 5 ' WinEvent action=5
' third hot key's definition
UserHotKeys(3, 1) = -50 ' alt-m
UserHotKeys(3, 2) = 6 ' WinEvent action=6
____________________________________
Notes: When REDIMing the UserHotKeys array, the lower bound of the first index
MUST be 0. The 0th entry never contains hot key data, but it is required by
LangWin. The upper bound of the first index must be the number of user defined
hot keys required.
User defined hot keys are active for ALL windows until the contents of
UserHotKeys are changed or UserHotKeys is REDIMed to have 0 entries [i.e.,
REDIM UserHotKeys(0, 1 to 2) ]. By changing the contents of UserHotKeys "on the
fly" you can dynamically change the GLOBAL hot keys and/or their action codes,
but this is NOT recommended (at best, it would produce an inconsistent
interface to your users since hitting a given hot key would produce different
results at different times).
You should not choose return codes 1, 2, or 3 for any user hot keys.
You cannot define any hot keys that are already used by WinEvent.
The keys used by WinEvent are:
Ctrl-M --- generates code 13 which is ENTER (selects button with focus)
Ctrl-I --- generates code 9 which is TAB (gives focus to next object)
Ctrl-[ --- generates code 27 which is ESC (close window event)
Ctrl-a --- causes an "about" window to be displayed
Ctrl-PgUp - generates a code -132 (gives focus to next window)
5.14 WinNum
-----------
This structure is DIMed as follows: WinNum(1 to MaxWindows). It is used as a
cross reference between window handle and window number (see Section 5.7 for a
discussion on the difference between these two values). WinNum(i) contains the
window number for window handle i. WinNum is new with V2.0.
--------------------
6.0 ADVANCED WINDOWS
--------------------
Well, if you made it this far in LangWin's user's guide, you are a true hacker
(I use that term, as it was originally defined when computers had vacuum tubes,
as a compliment for someone who truly wants to understand and use all of the
intricacies of a system). I'll share a few hints, tips, and secrets for getting
more miles/gallon from LangWin. Most of this section was re-written for
LangWin 2.0; I encourage users of LangWin 1.x to read this section is detail.
In addition, you should read the description of every routine in WINHELP.BAS
(not every LangWIn routine is described in this user's guide).
6.1 Adding Frills to Your Windows
---------------------------------
LangWin has a number of routines for adding frills to your windows. I'll cover
some them briefly here. Load WINHELP.BAS into QB and hit F2 to see a detailed
description of ALL routines (i.e., all of LangWin's routines are not described
in the user's guide; WINHELP.BAS is the real reference guide for LangWin).
In general, you can specify the position, color, and contents of various
objects to be placed in the window . These routines should be called just after
you create a given window and before you create another window or wait for
events.
ChangeButtonFocus - reverse video a button's text to show it has focus
MakeBox - place a single/double line box in the window
MakeHorizLine - draw a horizontal line across the window
MakeVertLine - draw a vertical line across the window
ShowTitle - place a title, centered, at top of the window
ShowWinText - place some text in the window
See SAMPLE0x.BAS, included on your LangWin distribution disk, for sample code
that uses the above routines.
6.2 Window Modes, Shadows, and Movement
---------------------------------------
With LangWin 2.0, there are four modes that can be assigned to a window
when it is opened (see descriptions of BlankWin and OpenScrollWindow
for calling parameters and values):
Modeless: This is a "normal" window. When it has focus, you can click on
any other visible window, and the new window will be given
focus.
Modal: When a modal window has focus (just after it is opened),
clicking on any other visible window will NOT produce an event.
This type of window can be used to display an error or warning
message that you want the user to read and acknowledge (by
either clicking on an "OK" button in the window, or by double
clicking the close icon). Your code should then close the
window. In most cases, the modal window ALWAYS retains focus
until it has been closed. Clicking other windows will NOT give
them focus. (An exception would occur if you placed a button in
a modal window that caused another window to be opened. This new
window could any mode - which might result in some interesting
bugs, so I don't recommend opening any new windows while a
modal window has focus. After any event occurs in a modal
window, just close the window. The point is that while a modal
window has focus, NO OTHER window can be given focus or
generate an event.
Immediate Close: When this window has focus, clicking on any other visible
window will automatically generate a close action code (just
as if the close icon was double clicked). The immediate close
type window can be used to display pull down menus. If a pull
down menu is visible, and the user clicks elsewhere, a close
action code is returned and the pull down menu is closed
(by your program's responding to the close action code). If you
want your pull down menu to remain on the screen when the user
clicks elsewhere, make it a modeless type.
Wallpaper: This is a static or background only window. It can NEVER get
focus, NEVER have any actions, and NEVER be moved. WinEvent
recognizes when a wallpaper window is clicked and ignores the
action. Wallpaper windows can be used to display static
information or as background for other windows (see
Section 6.3). You do not need to test for any events in a
wallpaper window. If you need to reuse the screen space
occupied by a wallpaper window, you will have to manually make
it current and close it (since the user cannot directly cause a
close event for that window).
Wallpaper window mode can only be selected from the BlankWin
routine (OpenScrollWindow cannot create a wallpaper window).
It goes without saying that you should not create any objects
(buttons, etc) in a wallpaper window (you can place static
text in a wallpaper window). Since wallpaper windows cannot
have any actions, you cannot create a wallpaper window with a
close icon.
In addition to the window's mode, the window can have three other attributes:
shadow/shadowless, movable/unmovable, and sizable/not-sizable. These are self
explanatory. The shadow feature is controlled by the sign of the mode parameter
in BlankWin and OpenScrollWindow. If the mode parameter is negative, the window
is shadowless. The movable feature is controlled by the sign of the window
color parameter in BlankWin and OpenScrollWindow. If the window color is
negative, then the window will be unmovable. The sizable feature is controlled
by the sign of the border color parameter in BlankWin and OpenScrollWindow.
A negative value will prevent the window from being resized (in addition, if
the window is unmovable, it cannot be resized - regardless of the state of the
sizable flag).
6.3 Using Wallpaper Windows
---------------------------
Wallpaper windows can NEVER get focus and NEVER generate any actions. Two
possible uses are: Information Only windows and background windows (to give
the "illusion" of multiple scrollable lists in one window). The following
sections describe these two techniques.
6.3.1 Information Only Windows
------------------------------
You should use WinEvent to wait for an event from any open window. WinEvent
will return the window's number and an action code. These will define where the
event occurred (window number) and what object caused the event (action code).
In general, after control returns from WinEvent, you must test for every
possible window number that could be open by comparing the window number
returned from WinEvent (which defines the window that had focus when the event
occurred) to the set of unique window numbers that were returned by all of the
BlankWIn or OpenScrollWindow routines used to create windows.
If, however, you wish to create an "information only" window that can NEVER
have any user actions (including closing it!), then you need not test for the
information window's number in your WinEvent loop. In the simplest case, where
you only want to place an info window on the screen and no other windows, then
you don't even need a WinEvent loop. Making the Info Window's mode 4 will
insure that no events can occur from the information window.
Remember, the info window cannot be selected or closed by the user (you can
close it with the CloseWindow routine as long as you manually make it current
before calling CloseWindow - see CloseWindow in WINHELP.BAS for an example
of how to do this). Also, remember that if you have info windows open, the
AnyWinOpen global variable will remain TRUE. Thus, using "DO WHILE AnyWinOpen"
to control your WinEvent loop will never exit because the info window will
remain open (and AnyWinOpen will be TRUE) even after all other windows are
closed. In this case, you'll have to explicitly break out of the loop (i.e.,
EXIT DO) when you detect a close event (action=1) in your main window.
One example would be to display instructions that need to remain on the
screen for subsequent windows. Open a window and display the instructions (see
ShowWinText). Then open another window (which becomes the active window) and
loop through WinEvent waiting for events. If the instruction window is Mode 4,
it cannot be moved by the user. It should be positioned so that subsequent
windows do not overlay it (thus obscuring the information you wanted to
display). Remember that after you close all subsequent windows, the instruction
window again becomes the active window and should be closed.
6.3.2 Multiple Scrollable Lists In One Window
---------------------------------------------
LangWin only supports one scrollable list per window. There are times when
you'll want to provide the "illusion" of several scrollable lists in one
window. Open a shadowed wallpaper window, then open several scrollable lists
"on top" of the wallpaper window, using the same color as the wallpaper. Make
these windows shadowless and unmovable. The user will "see" one window with
multiple scrollable lists. Your program will be responding to several windows,
all of which happen to be of the same color as the wallpaper window. Since the
wallpaper window can NEVER be given focus (WinEvent makes sure of this), you
don't have to worry that user will click the wallpaper, make it current, and
cause it to "overlay" (i.e., hide) all other windows that were placed "over"
it. SAMPLE04.BAS contains code that implements this technique.
When you use the above technique to open several windows, giving the illusion
that they all are really one window, you will want that window to appear to be
modal. That is, all mouse clicks on windows other than the "multiple window"
should be ignored until the multiple window is closed (otherwise portions of
the multiple window would get overlaid, impacting the illusion that the
multiple window is one entity). The "multiple window" is just that: several
windows. How does one prevent mouse clicks from being accepted on windows
other than the "multiple window", while allowing mouse clicks on any of the
component windows that make up the multiple window? Good question! I'm glad
you asked. First, you can't just make all component windows of the multiple
window modal. This would result in only the last component of the multiple
window accepting mouse clicks (if the last component window is modal, WinEvent
will restrict mouse clicks that window).
If there are no other windows open when the multiple window is created,
there's no problem (because there's no other windows where the mouse could be
clicked). However, if other windows exist when the multiple window is created,
these existing windows must not be able to accept mouse clicks while the
multiple window is open. In order to accomplish this, temporarily set all
windows that exist prior to creating the multiple window to wallpaper mode. As
wallpaper, WinEvent will ignore any events in these windows. The components of
the multiple window can then be opened as modeless (allowing actions in any of
the components).
The following code shows this technique for one existing window whose number
is saved in Main1:
x=IsWinOpen(Main1,han) ' get Main1's handle
' assume you KNOW it's open, and no need to
' test return from IsWinOpen for TRUE.
omode=WinParms(han,19) ' save main window's current mode
WinParms(han,19)=4 ' set mode to wallpaper
' now open multiple windows and process events
.
.
.
' done with multiple windows, close them all
WinParms(han,19)=omode ' restore original mode
6.4 Modifying Scrollable Text While Its Window Is Open
------------------------------------------------------
All scrollable text is saved in the SaveText array (see Section 5.8). One use
for scrollable text is to present a list of items that can be selected for
later action (e.g., a list of files to be printed, deleted, etc). The user
would scroll through the list and select desired items by double clicking each
one. After all items have been selected, an "action" button would be clicked to
process all previously selected items.
In order to implement this function, you must first give the user feedback that
the text line clicked has actually been selected. One possible way to do this
is to include specific characters in the scrollable text that can be used to
denote selection or un-selection. For example, every line of scrollable text
could begin with [ ] (that is, left bracket, space, right bracket) if it is not
selected, and [X] if it is selected.
When a line of text is double clicked, it is given focus and control is
returned from WinEvent. The value returned from WinEvent is the window's
number. The value returned in the action code will be a 2. Your code would
examine the window's number and action code, and give control to the proper
code segment (see Section 3.2). At that point, CurWinPtr (a global variable,
see Section 5.6) will contain the current window's handle. SaveText(i,j) will
contain the scrollable text with focus [where: i=WinParms(CurWinPtr,18) and
j=WinParms(CurWinPtr,15)].
To denote selection of the text line in focus, your program would first have to
change the second character in SaveText(i,j) to an "X". The text line would
then begin with [X] to indicate selection. Actually, you would first want to
test the contents of the selected text line's second character, so you could
toggle a space to an X, and an X to a space (thus, the user could click on the
text and toggle the selection on/off).
Once you've changed the scrollable text's selection character (on or off),
you'll need to actually update the contents of the visible window. To do this,
you must re-write that line of text into the proper position of the window. The
ReShowText routine will accomplish the necessary refresh into the current
window. After you refresh the window, continue looping through WinEvent.
When the user finally selects the "action" button, WinEvent will return an
action code of 3. After determining which window and which button was selected,
you can take appropriate action on all text lines selected. Merely step through
every entry in the current window's scrollable text, examine the second
character, and if it's an X, take action. The current window's scrollable text
can be found in SaveText(i,x) where: i=WinParms(CurWinPtr,18) and x = 1 to
WinParms(CurWinPtr,17).
In addition, you should re-set the selection, by changing the X back to a blank
in the text array. Again, you must update the contents of the window to show
that all previously selected items have now been un-selected. The ReShowPage
routine will accomplish this task (ReShowText CANNOT be used since it ONLY
refreshes the current line of text that has focus, and you may need to refresh
many text lines). After you refresh the window, continue looping through
WinEvent.
SAMPLE02.BAS has sample code that implements the above technique.
6.5 Changing the Entire Scrollable Text Array While its Window is Open
----------------------------------------------------------------------
The previous section described how you can change individual lines of
scrollable text in an open window. It is also possible to replace an open
window's scrollable text with an entirely new set. The new set can either have
more, less, or the same number of lines. For example, if your window displays a
directory structure and contents, and the user changes directories, you may
want to show a completely different list of file names in the window.
The RefreshScrollText routine (new with V2.0) can be used to replace the
scrollable text in the current window with focus. If the window to be updated
is not current, you must manually make it current (see RefreshScrollText in
WINHELP.BAS for an example of how to do this). You must place the new
scrollable text into a string array whose LBOUND is 1. This string array is
used as a parameter to the RefreshScrollText routine (see WINHELP.BAS for
additional details). Run time errors will occur if the current window with
focus has no scrollable text or LBOUND of the string array is not 1. After
control is returned from RefreshScrollText, I recommend that you ERASE the
string array (passed as a parameter to RefreshScrollText) in order to save
memory. If the UBOUND of your string array is greater than MaxTextLines, then
only the first MaxTextLines of the array will be displayed, and the
MaxTextLines entry will appear as: (Incomplete List) to let your user know
that all data is not visible. See Section 5.3 for additional details on
programming to handle this case.
SAMPLE04.BAS contains examples of techniques to modify the contents of
scrollable arrays while a window is open.
6.6 Modifying the Contents of an Input Field While Its Window Is Open
---------------------------------------------------------------------
Similar to the technique for modifying scrollable text (see Section 6.4), you
might need to modify the contents of an input field based upon other events in
the window (for example, the WINCOLOR routine, included on the LangWin
distribution disk, has input fields that can be updated directly or
incremented/decremented by selecting a push button). When a push button is
selected, WINCOLOR updates the corresponding input field (i.e., increments or
decrements the current value and redisplays the updated value).
Your program's logic will have to determine the handle of the input field that
should be updated. This can be done in response to a specific event in the
window (i.e., when a specific button is clicked, you may need to update a
specific input field). Let's call that handle: Han. To modify the corresponding
input field's contents, just change the appropriate characters in
ButtonsText(Han). Remember that ButtonsText is a STRING array. If the contents
represent numeric data, use the VAR command to convert to numeric, modify, and
use STR$ to place the equivalent string values back into ButtonsText(Han).
After the ButtonsText array has been updated, the window must be refreshed. Use
ReShowInputField to accomplish this task (similar to ReShowText in Section
6.4). After the input field has been updated and redisplayed, continue with the
WinEvent loop.
6.7 Determining What Had Focus When WinEvent Returns Control
------------------------------------------------------------
When control is returned from WinEvent, you will need to determine two things:
which window had focus and what action occurred in that window. The following
sub-sections address these two event-driven programming tasks.
6.7.1 Determining Which Window Had Focus
----------------------------------------
As described in Section 5.7, the WinEvent function returns a value that
corresponds to the number of the window that had focus when an action occurred.
When every window is created (by either BlankWin or OpenScrollWindow), it is
assigned a unique window number (value returned by the function). You should
save these window numbers in unique variables (if the value returned is < 0,
then an error has occurred when creating the window, see Section 6.14 for more
details on handling errors). When control is returned from WinEvent, you need
to compare the window number returned (by WinEvent) to the set of all window
numbers saved when your windows were created. When you find a match, you know
what window had focus (and you can then determine which action occurred in that
window - see Section 6.7.2).
The easiest way to implement the above technique is with a SELECT CASE
structure. Assume that three windows have been created, and their numbers are
saved in variables: w1, w2, and w3. The following pseudo code will determine
which window had focus when an event occurs:
' open some windows
w1=BlankWin(parms)
w2=OpenScrollWindow(parms)
w3=BlankWin(parms)
do while AnyWinOpen ' loop as long as windows are open
' wait for an event, window number returned in variable: wnum
wnum=WinEvent(action)
select case wnum ' determine which window had focus
case w1
' process actions for window: w1
case w2
' process actions for window: w2
case w3
' process actions for window: w3
end select
loop
The above code handles static windows: w1, w2, and w3, that is windows
specifically opened in your code before waiting for events. Your WinEvent loop
must test for every static window that was opened. However, the code that
processes events for a specific window might also open windows dynamically,
that is under control of your end user and the events that are selected at run
time. Thus, your WinEvent loop must not only test for all possible static
windows, but also all possible dynamic windows.
For example, in the above code, assume that in window w2, a new window should
be opened (number saved in w2a) when a specific event occurs (a button click).
Then, your loop must also include a CASE statement for w2a (to handle any
events that could occur if/when window w2a is open and current). The code new
might look like:
' open some windows
w1=BlankWin(parms)
w2=OpenScrollWindow(parms)
w3=BlankWin(parms)
do while AnyWinOpen ' loop as long as windows are open
wnum=WinEvent(action) ' wait for an event
select case wnum ' determine which window had focus
case w1
' process actions for window: w1
case w2
' process actions for window: w2
.
.
.
if specific button pressed then
w2a=BlankWin(parms)'open a dynamic window under user control
end if
.
.
.
case w3
' process actions for window: w3
case w2a
' process actions for window :w2a
end select
loop
Thus, as you begin developing code segments to handle every possible event, you
will find yourself adding additional segments to handle dynamic windows that
can be open due to actions or errors.
6.7.2 Determining What Action Occurred In The Window
---------------------------------------------------
WinEvent takes one parameter. Upon return, this parameter is set to an action
code: 1=close; 2=scrollable text; 3=button.
Typically, the code segment for each window will have to test the action code
(returned by WinEvent) and take appropriate action. Again, the SELECT CASE
structure can be used. The following pseudo code would reside within the
segment for a given window (window w2 is used in the example):
.
.
do while AnyWinOpen ' loop as long as long as windows are open
wnum=WinEvent(action) ' wait for an event
select case wnum ' determine which window had focus
.
.
case w2
' process actions for window w2
select case action
case 1
' process the close action in window w2
case 2
' process the selected scrollable text in window w2
case 3
' process the button clicked in window w2
end select
.
.
end select
loop
If the action code was 2, then a line of scrollable text was selected (i.e., it
has focus). The selected line of text can be found in SaveText(i,j); where:
i=WinParms(CurWinPtr,18) and j=WinParms(CurWinPtr,15). In this case, no button
could have had focus (i.e., when a line of text is clicked, focus is removed
from any button). Process SaveText(i,j) as necessary (see Section 6.4 for an
example).
If the action code was 3, then a button was clicked. Your code segment must
determine which button has focus (if there were more than one buttons in the
window). WinParms(CurWinPtr,16) contains the handle of the button with focus.
Use this value in a SELECT CASE to process every possible button created in the
given window. Button handle values are returned by the MakePushButton routine
and should be saved in unique variable names. These variables will then be used
in your CASE statements. If a button was clicked, it is given focus and control
returned from WinEvent. In this case, scrollable text could also have focus (if
scrollable text exists). Your code segment will have to determine if any
actions should be taken with any scrollable text that also has focus.
The following code segment shows how you might process a close action, clicking
on scrollable text, or clicking on a button in a given window (w2). If button
w2b2 is clicked, a dynamic window is opened.
.
.
w2=OpenScrollWindow(parms) ' open a window
w2b1=MakePushButton(parms) ' create buttons in
w2b2=MakePushButton(parms) ' the window
.
.
do while AnyWinOpen ' loop as long as windows are open
wnum=WinEvent(action) ' wait for an event
select case wnum ' determine which window has focus
.
.
.
case w2 ' process events in window: w2
select case action ' determine which action occurred
case 1
' process the close action in window w2
xx=CloseWindow ' close the window
case 2
' process the selected scrollable text in window w2
slot=WinParms(CurWinPtr,18) ' slot with window's text set
row=WinParms(CurWinPtr,15) ' row of text with focus
process SaveText(slot,row)
case 3
' process the button clicked in window w2
select case WinParms(CurWinPtr,16) 'determine which button
case w2b1
' process a click on button: w2b1
case w2b2
' process a click on button: w2b2
' open a new window if button w2b2 is clicked
w5=OpenScrollWindow(parms)
end select
end select
.
.
end select
loop
Once you understand the above technique, refer to SAMPLE0x.BAS, included on the
LangWin distribution disk, for complete code that implements windows. If you
need to reference LangWin's routines and their actual parameters, load
WINHELP.BAS (which was included on LangWin's distribution disk) into QB and hit
F2.
6.8 Nesting Calls to WinEvent
-----------------------------
As you can see from the previous code examples, things can get complex very
quickly. Your WinEvent loop must have a CASE for every possible window (both
static and dynamic). Within each code segment that processes a window, you need
CASE statements for every possible action code (at least codes 1-3 plus any
codes for user defined hot keys). Then, within the code segment to handle
action 3 (button click), you need a CASE statement for every button created in
that window. For a complex set of windows, with several menus, sub-menus,
buttons, user hot keys, and dynamic error windows, your code will be long.
One way to make your code more readable is to perhaps limit the main module to
just the CASE statements for all windows. Once you determine which window had
focus, you could call a separate subroutine to process each window. The
subroutine would have to be aware of the button handles created for its window
in the main program, and if the subroutine opens any windows and saves their
handles in variables, the main routine must have access to these variables
(because the main module needs a CASE statement for every possible window). A
good exercise in sharing variables.
The subroutine that processes events for a given window could create child
windows and have its own WinEvent loop. In this case, that WinEvent loop would
only be "aware" of events in windows it was programmed to respond to (i.e.,
child windows opened while control was in the subroutine). Clicking other
windows and objects would have no effect until the subroutine returned control
back to the main module. In some circumstances, this technique might not only
make your code simpler to understand/debug, but may be exactly the method you
need to process child windows. Care should be taken in nesting calls to
WinEvent (from within a WinEvent loop, calling a subroutine that has its own
call to WinEvent and loop, which in-turn calls a subroutine with a WinEvent
loop, etc.). I've had stack overflows when trying to nest too many calls to
WinEvent. (What "too many" is depends upon memory availability. I'd recommend
that you don't nest more than 3 or 4, after that your code is probably far too
complex to deal with anyway).
6.9 Deactivating and Activating Buttons
---------------------------------------
The pseudo code in the previous sections demonstrated how you might open a
dynamic window when a specific event occurred (i.e., a button click).
Typically, when this new window has been opened, you may want to prevent the
end user from continuing to click the same button and opening more windows of
the same kind (this could quickly cause MaxWindows, maximum number of windows
that can be open, to be exceeded and perhaps your program to fail). One way to
prevent additional windows from being opened when the same button is clicked is
to make the window's mode either Modal or Immediate Close (see Section 6.3 for
details). In the case of a Modal window, the user MUST close the window before
any other event can take place (thus clicking the same button again is not
possible until the dynamic window is first closed). In the case of an Immediate
Close window, clicking the same button will first result in a close action for
the dynamic window (i.e., clicking anywhere off the Immediate Close window
will cause it to be closed).
Both of the above techniques (Modal and Immediate Close) have their
disadvantages. In particular, you may want to allow the user to mouse elsewhere
(while the new dynamic window is open) and select other objects (except the
button that originally caused the dynamic window to be opened). You may also
want the new dynamic window to remain open while the user mouses elsewhere.
To get around these disadvantages, you can use the DeactivateButton routine.
This will clear the button's text and make it inactive. While the button is
inactive, no action on that button will be recognized by WinEvent (i.e.,
clicking the button will not cause a return from WinEvent). When you are ready
to accept actions from the given button again (i.e., after the new dynamic
window has been closed), then call the ActivateButton routine. See WINHELP.BAS
for details on how to call these routines.
6.10 Giving a Specific Button Focus When It Is Created
------------------------------------------------------
When buttons or check boxes are created (with the corresponding "make"
routine), they are not given focus. Hitting TAB or an arrow key will cause the
"next" object to get focus (and be displayed in reverse video). Hitting ENTER
will then cause control to be returned from WinEvent with an action code of 3.
WinParms(CurWinPtr,16) will contain the handle of the button in focus. Clicking
on the button will also cause these events to occur.
As you create buttons in a window, you might want to give one of them
focus (i.e., make it the default choice). Your user could then click this
button to select it, or just hit ENTER (since it will already have focus).
The following code should be used to give a specific button focus (assume
that the button's handle, returned from the corresponding "make" routine,
is saved in Han):
WinParms(CurWinPtr,16)=Han ' place handle in data structure
CALL ChangeButtonFocus(Han,0) ' give button visual focus
The above code MUST be placed AFTER the corresponding window has been created
(with BlankWin or OpenScrollWindow), AFTER the button has been created (with
a "make" routine), and BEFORE the next window is created (i.e., CurWinPtr
must point to the current window where the button will be given focus - see
Section 5.6).
Only ONE button (or check box) per window can be given focus.
WinParms(CurWinPtr,16) contains its handle. You could call ChangeButtonFocus
multiple times to reverse video many buttons, but only the button whose handle
is found in WinParms(CurWinPtr,16) will be recognized by WinEvent when
processing mouse or keyboard input. (Calling ChangeButtonFocus for more than
one button will only confuse your user).
6.11 Is A Given Window Open
---------------------------
On occasion, you may need to determine if a specific window is open. For
example, if you receive a click on an EXIT button, you may want to determine if
any child windows have been opened dynamically (and close them first). Of
course, you could have deactivated the EXIT button (see Section 6.8) when a
child window is opened (which will prevent EXIT from being clicked until you
activate the button when the child window is closed). However, let's assume
that you will allow the user to click the EXIT button and automatically close
any dynamically opened child windows along with the main window.
The IsWinOpen function (new with 2.0) can be used for this purpose. Its input
parameter is the window's number (NOT the window handle). It returns a TRUE
value if the window is open; else it returns a FALSE value. IsWinOpen also
takes a second (output) parameter. If the window number passed is open, the
second parameter is set to the window's handle (thus IsWinOpen will also
provide a window number to window handle cross reference function - also see
Section 5.14).
See WINHELP.BAS for additional details on IsWinOpen.
6.12 Manually Giving a Window Focus (i.e., via program control)
---------------------------------------------------------------
When WinEvent has control, it will detect when the mouse is clicked on a window
and (if appropriate) give that window focus (so that any subsequent objects
selected in that window will cause control to be returned). The global variable
CurWinPtr contains the handle of the window with focus.
There may be occasions when your program will need to cause a specific window
to obtain focus. The NewFocusWindow routine takes a window's handle as its
parameter, and will cause that window to be given focus.
For example, if a main window has an EXIT button, and the button is selected,
then the main window should be closed. However, before the main window is
closed, all open child windows should probably be closed (of course there are
exceptions to this "rule"). The IsWinOpen (see previous section) can be used to
determine if a specific child window is open (given the child window's number),
and if so it's handle will be returned. Using that handle of each open child
window, NewFocusWindow can be used to first give it focus, then close it (the
CloseWindow routine closes the current window, so each child must first be made
current before it can be closed).
WARNING: NewFocusWindow will cause your program to terminate if an invalid
handle is passed to it. The handle must correspond to an open window. One way
to insure that you have a valid handle is to use the IsWinOpen routine which
will return the handle of an open window (given that the window is open and you
pass IsWinOpen it's window number).
6.13 State of Check Boxes
-------------------------
Clicking on a check box will change its state, but will NOT return an event
from WinEvent. However, when an event does occur and control is returned from
WinEvent, you may want to check the state of any check boxes in the current
window. If you defined a check box in the current window, and saved its handle
value (returned by MakeCheckBox) in C1, then you can test the value of
ButtonsData(C1,7) after WinEvent returns control (regardless of the event) to
determine the state of the check box. If ButtonsData(C1,7)=0, then the check
box has no shadow and it is down. If ButtonsData(C1,7)=1, then the check box
has a shadow, and it is up.
6.14 Run Time Errors
-----------------------
Many of LangWin's functions return codes indicating success or failure. For
those routines whose parameters are static (not changable based upon user input
at run time, but constants hard-coded in the parameter lists), you'll find most
errors during development. For example, if a window fails to open, you can
insert a breakpoint, check the return code, and figure out the error (i.e.,
window won't fit on the screen, etc.). You could also place instructions after
the CALL to these functions, test the return code, and print it if an error
value is indicated (see WINHELP.BAS for a description of every routine and its
return codes). I'd recommend this technique (i.e., testing return codes for
error values) in all cases where the parameters to LangWin's functions are
dynamic (i.e., based upon conditions that can be changed during run time).
If a window fails to open (for example, because it will not fit on the screen),
and you don't test for this error, then subsequent references to LangWin's data
structures using CurWinPtr will reference the last window with focus. This will
either result in a run time error (subscript out of range) or unpredictable
results in your program.
For example, with V2.1 resizing is supported. In addition to saving objects
(i.e., buttons, check boxes, input fields) in data structures, window titles
and static text must also be saved (new with V2.1). The global parameter
MaxButtons determines the size of these data structures. If MaxButtons is too
small, then the routines that create objects, titles, and static text will
return an error code and the object will not be created. If you don't test for
this error, you'll have windows that will be missing some/all expected objects
(i.e., you could end up with an empty window). While this won't cause a fatal
error, your windows might not contain the information you expect or need. All
of the windows/objects created prior to entering the WinEvent loop can be
visually examined to determine if all objects are present. However, if within
the WinEvent loop you open child windows based upon the user's dynamic input
(i.e., clicking a button), then you might need to visually examine a large
number of combinations to insure that every possible set of windows that could
be created on the screen has the correct objects (i.e., that MaxButtons is
large enough). Thus, while it is not too important to check error codes prior
to the WinEvent loop (because all windows and objects are statically created
and can be checked visually), within the WinEvent loop I'd recommend more
careful error checking.
In general, I always check for error conditions when a window is opened (i.e.,
BlankWin or OpenScrollWindow). When I call any other routine with dynamic
parameters determined at run time (usually within the WinEvent loop), I also
check for error codes.
LangWin will generate a run-time error and abnormally terminate if any of the
following conditions occur (routine names generating the error are given in
parentheses):
* MaxTextWins is greater than MaxWindows (LangWinInit)
* LBOUND of string array passed as a parameter is not 1 (OpenScrollWindow
and RefreshScrollText)
* Current window with focus has no scrollable text (RefreshScrollText)
* Invalid window handle passed as a parameter (NewFocusWindow)
All of the above conditions can be avoided by careful programming (unless, of
course, Murphy is lurking nearby!). Refer to the corresponding member in
WINHELP.BAS for more details on these errors.
6.15 Testing for Color, B/W, EGA, or VGA
----------------------------------------
LangWin requires a color monitor and EGA or better graphics. Here's a technique
for determining if your user is running on a system with a color or black and
white monitor:
def seg = 0 ' low memory
if peek(&h463) = &hB4 then mon$="b/w"
if peek(&h463) = &hD4 then mon$="color"
def seg
Determining whether EGA or better graphics exists is less straight forward. One
technique is to use ON ERROR and to vary SCREEN modes (i.e., 12, 9). Depending
on which mode (if any) causes an error, you can determine which color graphics
support exists. I don't really like this technique. It's "brute force", but it
works.
Another technique is to use BIOS interrupt 10h, functions 1Ah and 10h. I've
had success with this technique, but my reference manuals say function 1Ah is
for PS/2s. I've tried it on a non-PS/2 and it worked ok, but I can't say that
it will work on every non-PS/2. To use this technique, first use the above
test to determine if the monitor is color or b/w. Then call BIOS interrupt 10h
function 1Ah. If the AX register is set to 1Ah upon return from the interrupt,
then the function is supported by your BIOS. In that case, test the value of
BX (4=EGA color, 5=EGA b/w, 7=VGA b/w, 8=VGA color). If AX is not set to 1Ah,
then call BIOS interrupt 10h, function 12h, sub-function 10h. If BX is
returned with a value of 1, then you have a b/w monitor. Else, if BX is not
10h, then you have EGA color.
6.16 Colors, Attributes, and Palettes
-------------------------------------
Throughout ALL of your code, you MUST use the SetColor routine instead of the
COLOR command to change color attributes. The parameters are the same
(foreground and background attributes) as the COLOR command. LangWin uses BIOS
interrupt 10h, function 10h, sub-function 03h to disable blinking colors. This
allows 16 attribute numbers for window (background) colors (rather than just
8). However, with blinking disabled, foreground and background attribute
numbers MUST be translated before calling BASIC's COLOR command. SetColor does
this translation and issues the COLOR command with the proper values. If
BASIC's COLOR command is called without this translation, you will get
unexpected colors.
While in the QB development environment, if you interrupt your program under
development, and use F4 to see the data displayed on the screen, you'll see
blinking colors for background attribute numbers > 8. This is ok. As previously
mentioned, LangWin disables the blinking bit and uses blinking background
attributes (9-15) for additional colors. The QB environment, however, does not
disable the blinking bit. So, when you hit F4 to see the screen, background
attribute colors > 8 will blink. If you hit F5 to resume, the colors will still
blink (because the blink bit is only disabled once in LangWinInit). If you
restart your program (Shift-F4), then LangWinInit will again disable the
blinking bit and colors will appear without blinking.
LangWin supports 16 attributes (0-15) for colors. In text mode, however, you
can assign up to 64 color values (0-63) to any given attribute number via the
PALETTE command (i.e., PALETTE 5,27 assigns color number 27 to attribute 5).
I've included a program called WINCOLOR on the distribution disk that will
allow you to see all possible color values for various window entities. Once
you find a set of colors values (as opposed to color attributes) that look
good, then use PALETTE in your program to assign the color values to the
attribute numbers coded in CALLs to LangWin routines.
For example, MakePushButton requires two attributes as parameters (foreground
and background of button). Assume you use attributes 2 and 3 for these
parameters. Using WINCOLOR, you might determine that color values 23 and 41
make pleasing foreground/background colors for your button (I just picked those
two numbers at random and have no idea how they actually look). Using this
example, you would code: PALETTE 2,23 (to set attribute 2 to color 23) and
PALETTE 3,41 (to set attribute 3 to color 41). You would NOT need to modify
your parameter list for MakePushButton (which would contain the attribute
numbers 2 and 3).
SAMPLE02.BAS has code that illustrates this technique.
6.17 WaitTicks
--------------
QuickBASIC has a command (SLEEP) that will allow your code to pause a given
number of seconds. LangWin includes a routine (WaitTicks) that will allow your
program to pause a given number of timer ticks (one timer tick is approximately
1/18.2 seconds). If you need finer granularity than seconds in a delay loop,
use WaitTicks.
6.18 Video Pages
----------------
If your video board has enough memory, you can display multiple video pages.
Refer to your BASIC documentation on the SCREEN command. With V2.3, LangWin
will support windows in any video page that is available on your system;
however, you MUST only use one video page consistently for all of LangWin's
displays. That is, you should not switch video pages in the middle of a
program that uses LangWin to display windows. This could cause unpredictable
results. You MUST also define the video page via a SCREEN command BEFORE
the call to LangWinInit.
LangWin keeps track of the coordinates of every object (button, check box,
input field, etc.), but it does not remember the video page for these objects.
LangWin assumes that all objects are in the same video page (I could add a
feature to remember individual video pages for all objects, but LangWin's data
structures are already getting too large). During initialization, LangWinInit
obtains the current video page and saves this information in a global
variable. All routines that read/write data directly in the video buffer use
this global variable to place the data in the correct page. These routines
will continue to write into the original video page regardless of the change
in page number via the SCREEN command. However, some of LangWin's routines use
the standard PRINT command to display data. These routines will display their
data in the new video page. So, some data will be visible in the new page and
some won't. Like I said, unpredictable results.
In addition, if you change video pages with the SCREEN command, and happen to
click on the coordinates of an object in the original page (which will be
invisible at this point), LangWin will recognize the object and return control
from WinEvent. Hitting enter will cause similar results with whatever object
had focus (which might be on an invisible page). In these cases, your code
will get control from WinEvent and process the event, even though the actual
button might have been on an invisible page. Again, you could get
unpredictable results. So, my advice is not to change video pages once the
page number has been initially defined.
The page number must be initially defined via a SCREEN 0,,x,x command (where x
is a valid video page supported by your system). With V2.3, this command MUST
be placed BEFORE the call to LangWinInit, which detects the video page and
saves it in a global variable (actually the offset in the video buffer is
saved in a global variable). See Section 3.1 for sample initialization code.
There is one exception where changing video pages might be useful. If you plan
to display data without the use of LangWin's routines, then you could change
pages, display the data, then (when the user is finished with the data),
change back to the page containing LangWin's display. For example, suppose you
use LangWin to display a window with a scrollable list of file names. When the
user clicks on a file name, you may want to display the file. One technique
would be to change page numbers, use the SHELL command to exit to DOS and call
a viewer to display the selected file. When the user exits the viewer, control
would be returned from DOS back to your program at the point following the
SHELL command. You would then reset the video page back to the original value
and the scrollable list of files would again be visible. Before SHELLing to
DOS to call the viewer, you should check for a mouse and hide the cursor. Upon
return, check again and show the cursor. Here's a prototype of this example:
' prior to this code, you would have detected a scrollable text click
' (action=2) in the appropriate window.
i=WinParms(CurWinPtr,18) ' index of clicked text block
j=WinParms(CurWinPtr,15) ' specific index of text line
filespec$=SaveText(i,j) ' text that was clicked
IF HaveMouse THEN CALL HideMouseCursor ' hide mouse
SCREEN 0,,x,x ' set to new page (you must define x)
SHELL "view "+filespec$ ' call viewer (assume its in your PATH)
SCREEN 0,,z,z ' z is the original page number
IF HaveMouse THEN CALL ShowMouseCursor ' show mouse
The above code is only a prototype. It obviously depends upon exactly what
your viewer does. It's meant to give you an example of how you might use
multiple video pages with LangWin.
As an alternative to changing pages when shelling to an external program, you
could open a window that covered the full screen. This would cause the entire
screen's contents to be saved. Upon return from the external program, just
close the current window. At that point, whatever the external program left on
the screen would be visible; however, closing the current window would force
LangWin to restore the contents of the screen "underneath" the last window
opened. Since the last window opened covered the full screen, the effect of
closing it would be to restore the entire screen (containing all windows that
existed prior to shelling to the external program). The following code
implements this technique for the file viewer example:
' prior to this code, you would have detected a scrollable text click
' (action=2) in the appropriate window.
i=WinParms(CurWinPtr,18) ' index of clicked text block
j=WinParms(CurWinPtr,15) ' specific index of text line
filespec$=SaveText(i,j) ' text that was clicked
IF HaveMouse THEN CALL HideMouseCursor ' hide mouse
xx=BlankWin(1,1,MaxRows,MaxCols,0,0,1,0,0,-1)
' the above code will open a window that covers the entire screen.
' the window's color will be 0 (black).
' MaxRows and MaxCols are global variables defined by your initial
' call to LangWinInit.
' the effect will look like the screen has been cleared.
' the purpose of this call to BlankWin is to cause the contents
' of the screen "under" the window to be saved. that is, the
' entire screen will be saved. when this window is subsequently
' closed, the screen's contents will be restored.
SHELL "view "+filespec$ ' call viewer (assume its in your PATH)
x=CloseWindow ' close full screen window which will
' restore the screen
IF HaveMouse THEN CALL ShowMouseCursor ' show mouse
6.19 Using Non-Text Mode Graphics With LangWin
----------------------------------------------
It is possible to "jump" into non-text mode graphics (i.e., SCREEN 9,
SCREEN 13, etc.) then back to text mode (i.e., SCREEN 0). However, be aware
that when you jump to graphics mode, the screen will be cleared. I'll show you
a technique for restoring the screen once you return to text mode, but when
you jump from text to graphics, the contents of the screen (i.e., all windows
and objects that have been displayed) will be cleared (but not lost). When you
jump back from graphics mode to text mode, the contents of the graphics screen
will be cleared and lost (unless you manually save the screen yourself).
Finally, remember that LangWin's routines cannot be used while in graphics
modes.
Assume that you have opened some windows and placed objects in those windows.
Also assume that you are in the loop that calls WinEvent and tests for the
window number and action code. Depending upon a specific action (say a button
click), assume you need to go into SCREEN mode 9, display some graphics, get
user input, and then return to text mode. When you return to text mode, you
want to see all windows and objects that existed before going to graphics
mode. The following prototype code can be used:
' you determine that graphics mode is needed
xx=BlankWin(1,1,MaxRows,MaxCols,0,0,1,0,0,-1)
' the above code will open a window that covers the entire screen.
' the window's color will be 0 (black).
' MaxRows and MaxCols are global variables defined by your initial
' call to LangWinInit.
' the effect will look like the screen has been cleared.
' the purpose of this call to BlankWin is to cause the contents
' of the screen "under" the window to be saved. that is, the
' entire screen will be saved. when this window is subsequently
' closed, the screen's contents will be restored.
SCREEN 9 ' jump to graphics mode (this will actually cause the
' the screen to be cleared. since it's already black,
' clearing a second time won't be noticed).
' do your thing in graphics mode
SCREEN 0 ' back to text mode. contents of graphics screen will be
' cleared
WIDTH MaxCols, MaxRows ' reset rows/cols
' MaxRows and MaxCols are global variables defined by your initial
' call to LangWinInit.
CALL BlinkOff
' the BlinkOff subroutine is included in LangWin. its purpose is to
' tell the BIOS to turn off the blink bit in the color attribute
' (see Section 6.16 for additional information). if you don't
' call BlinkOff here, and you've used any background color attributes
' greater than 7, then these colors will blink when your windows
' are restored
CALL SetTextCursor(DefaultSmask, DefaultCmask)
' the above code will set the mouse text mode cursor to a white
' arrow on a black background. DefaultSmask and DefaultCmask are
' global variables defined by your initial call to LangWinInit.
CALL ShowMouseCursor ' re-enable the mouse cursor
xx=CloseWindow
' the above code will close the window that covered the screen.
' this will restore the screen's contents.
' your windows and objects should appear just as they did before
' jumping to graphics mode
6.20 Closing Windows
--------------------
Normally, you'd just call CloseWindow to close the current window with focus.
However, you may need to close some other window, or you may not be sure
that the window you want to close has focus (remember, the used could click on
any window and give it focus). Assume that you know the number of the window
to be closed (the window number is returned by the function used to open it).
If the window's number is saved in variable WN, then the following technique
can be used to make sure that the window has focus before calling CloseWindow:
IF IsWinOpen(WN,han) THEN ' if window is open, get its handle --> han
zz=CurWinPtr ' save handle of current window with focus
CALL NewFocusWindow(han) ' make the target window active
x=CloseWindow ' and close it
CALL NewFocusWindow(zz) ' return focus to original window
END IF
If you wanted to close several windows, and you did not know which
(if any) were open, you'd have to repeat the above code for each possible
window number to make sure all windows were closed. In this case, you could
just save the current window's handle once [ zz=CurWinPtr ] and restore it
after all windows were closed [ CALL NewFocusWindow(zz) ].
There's another "short-cut" you can use if you just want to close ALL open
windows (if you want to close a sub-set of all possible windows, you'll have
to use the above technique with the numbers of each window to be closed).
To easily close ALL open windows:
FOR i = LastWinStack TO 1 STEP -1
CALL NewFocusWindow(WinStack(i))
x=CloseWindow
NEXT
The WinStack array contains the handles of all open windows, in the order
in which they appear on the screen. LastWinStack is a global variable pointing
to the current slot. Thus WinStack(1) contains the handle of the first window
opened, and WinStack(LastWinStack) contains the handle of the window with
focus. The above code just closes each window, from the most current to the
least current.
6.21 Modifying Static Text Created With ShowWinText
---------------------------------------------------
The ShowWinText function is used to place static text in a window. Parameters
include relative row/column (within the window) where the text is to be
placed, text color, and text contents. The static text can be modified/changed
by calling ShowWinText with the same row/column but new text. This is
acceptable when only a few updates are needed. However, if the static text is
modified based upon some user action (like a button click), then there is no
way to determine the number of times the text will be modified. Each time
ShowWinText is called, the text is saved in LangWin's data structures
(ButtonsText and ButtonsData). These structures have a finite dimension
(MaxButtons). Each call to ShowWinText requires a new entry in the data
structures. Thus, repeated calls to modify text can exhaust all available
slots. In this case, the result would be that subsequent calls to create
objects (either new buttons, input fields, check boxes, or static text) will
fail and the object will not appear. Closing a window will free all slots
occupied by its objects. Increasing the value of MaxButtons will make more
slots available. However, neither of these is a good solution. A better
solution is to re-use the same slot whenever existing static text is modified.
All that is needed is to first determine the slot number (handle) of the
static text, then to update the contents of the existing slot and re-display
it.
Since ShowWinText returns an error code and not the handle of the slot used to
store text, you'll need to find and save the handle number in order to later
modify its contents. The best way to do this is to use ShowWinText to
temporarily place some specific text into the data structures, then manually
scan the data structure until you find the specific text. The following code
will accomplish these tasks:
x=ShowWinText(r,c,colr,"SOME UNIQUE TEXT")
' the above will place "SOME UNIQUE TEXT" at row=r, column=c in the
' current window using color=colr. the string "SOME UNIQUE TEXT"
' will also be placed into the next available slot in ButtonsText
IF x < 0 THEN
' if error condition, process it
END
END IF
thandle = -999 ' set a default value for the handle
' now search ButtonsText for the specific text string
FOR i = 1 TO MaxButtons
IF ButtonsText(i) = "SOME UNIQUE TEXT" THEN
thandle=i ' save handle number
EXIT FOR ' quit serach
END IF
NEXT
' just in case the "impossible" happens
IF thandle = -999 THEN
' process the error
END
END IF
At this point, the variable: thandle points to the slot in LangWin's data
structures that contains the temporary static text. The screen itself displays
the temporary text at the designated row/column. Now, you must replace the
temporary text with the initial value of the static text (using the same slot
in the data structures) and display this text on the screen. The following
code will accomplish these tasks:
a$="initial contents of static text field"
ButtonsText(thandle)=a$ ' update text in data structure
CALL ReShowInputField(thandle) ' redisplay text on screen
ButtonsData(thandle,4) = LEN(a$) ' update length of text area
You might ask: why is the length of the text in LangWin's data structure
updated AFTER the text is displayed? Glad you asked that question!
ReShowInputField (which is really meant to redisplay the contents of an input
field after it has been modified, but will also redisplay static text) first
clears the entire contents of the area, then displays the new text. The length
of the area to clear is obtained from the data structure. So, if the length of
the new text was smaller than the previous text, and the new (smaller) length
was first placed into the data structure, then only the smaller area would
cleared prior to displaying the new text. Any characters from the previous
(longer) text beyond the end of the new (smaller) text would remain on the
screen. By calling ReShowInputField before the length is updated, the length
of the area cleared will correspond to the the current text.
Later in your program, when you determine that new static text must be
placed into the window at the same position as the old text, the following
code can be used:
a$="new contents of static text field"
ButtonsText(thandle)=a$ ' update text in data structure
CALL ReShowInputField(thandle) ' redisplay text on screen
ButtonsData(thandle,4) = LEN(a$) ' update length of text area
These techniques can be used with any number of static text entries. You'll
just need to save each handle in a unique variable name.
6.22 "Time Out" Feature of WinEvent for Interrupt Buttons
---------------------------------------------------------
Normally, when you call WinEvent, your program only regains control when
WinEvent detects an event. If no event occurs, WinEvent retains control, and
your program cannot do any work. In most cases, this is ok. You don't need to
do any work until an event occurs in a window. Then, your program will get
control from WinEvent, determine the window and the event that occurred, take
the appropriate actions, and return control to WinEvent to determine the next
event in the window.
However, if the action you take involves starting a long running task, then
WinEvent will not be given control until this long running task completes, and
there is no way to interrupt the long running task (because WinEvent will not
have control, events in the window, like button clicks, will not be detected).
This is where the "time out" feature of WinEvent comes in. With the "time out"
feature enabled, if no event occurs within 0.5 seconds, WinEvent will return
control to your program. WinEvent is called with one parameter (the action
code). Prior to returning control to your program, WinEvent sets this action
code based upon the event that was detected (1 = close, 2 = text click, 3 =
button click). To enable the time out feature, call WinEvent with the action
code variable set to -999 (any other value for the the action code will have no
effect on WinEvent). If an event occurs, the action code variable is set
appropriately. If no event occurs, and the action code was -999, control will
be returned after 0.5 seconds (and the action code will remain set to -999).
WARNING: do not call WinEvent(-999) to enable the time out. You must call
WinEvent with a variable since this variable will be set if an event occurs.
To enable the time out feature, call WinEvent as follows:
ActCode=-999
wn=WinEvent(ActCode)
In order to interrupt a long running task, place the task in a loop with a
call to WinEvent (with time out feature enabled). Each time through the loop,
some portion of the long running task is completed (like reading one record
from a large file, scanning one directory, whatever portion of the overall
task that is appropriate). After completing a portion of the long running
task, call WinEvent with the time out feature. After the call to WinEvent,
determine what (if any) event occurred. If the event was a click on your
interrupt button, exit the loop that processed the long running task. If no
event occurred (i.e., WinEvent timed out after 0.5 seconds), then loop and
perform more work on the long running task.
Here's some pseudo code. Assume this code gets control when a "start" button
is clicked to initiate the long running task. Also assume that prior to this
point, a MODAL window was opened with start and interrupt buttons for the
long running task. Finally, assume that the long running task is to read all
records from a file.
CASE StartButton
deactivate "start" button (don't need it now)
activate "interrupt" button (assume it was previously inactive)
open a file (assume the long running task is to read a file)
' process long running task
DO
read a record (assume the long running task is to read a file)
update screen to show some kind of progress
if EOF then EXIT DO ' see if task is done
act=-999 ' to activate time out feature
wn=WinEvent(act) ' get an action or time out
LOOP UNTIL interrupt button action detected
' long running task completed or interrupted
deactivate "interrupt" button (not needed now)
activate the "start" button (if it's needed)
close file
process the data
close the current window (if it's no longer needed)
Note that you should not attempt to do too much work in your loop before
periodically giving control to WinEvent (to see if the interrupt button was
clicked). WinEvent is not aware of mouse clicks made before it is called. This
is because it must hide and re-show the mouse cursor during initialization
(which causes the mouse driver to zero the mouse press counter). Thus, if you
do a lot of processing before calling WinEvent, your user could have been
attempting to click the interrupt button for awhile with no response. You'll
have to experiment, and tune your code so that it gets back to WinEvent
quickly. If you notice that the interrupt button needs to be clicked very
frequently before it is recognized, then the cause is probably due to an
excessive amount of time consumed by your task in between iterations of the
loop where WinEvent is called.
I strongly recommend that the window you open, with buttons to start and
interrupt the long running task, be MODAL (that is, only events in that window
will be recognized by WinEvent, events in other windows will be ignored). The
"in-line" call to WinEvent (in the loop that processes your long running task)
will restrict the events recognized by your program to just those that you
explicitly test for after returning from WinEvent (the code you have elsewhere
in your program to handle other buttons in other windows will not be given
control). If the window with the start and interrupt buttons is not modal,
then your user will be able to mouse to other windows, make them active, and
click their buttons. This will have visual effects (active windows will move
to the top, clicked buttons will move); however, unless explicit code to
handle these events (in other windows) is included in your loop with the long
running task, the clicks on other buttons will be ignored. This will confuse
your user. By making the window with the start and interrupt buttons modal,
your user will not be able to mouse to another window to make it active, and
clicking objects on other windows will show no visual effect.
See SAMPLE05.BAS for an example of the technique for using WinEvent's time out
feature to implement an interrupt button.
6.23 Dynamically Adding Entries to a Visible List of Scrollable Text
--------------------------------------------------------------------
The OpenScrollWindow function is used to create a window with scrollable text.
After the window is opened, you may want to add entries to the end of this list
in real-time and display these new entries in the visible window.
The GrowScrollText function will accomplish this task. This function takes one
parameter: a text string. The contents of this text string is appended to the
end of the scrollable text array associated with the current window, and the
visible text in the window is re-displayed. The nature of that re-display
depends upon whether or not the existing scrollable text fills the text area
defined for the window.
If the list of scrollable text in the current window does not fill the text
area defined when the window was opened, then the text string passed to
GrowScrollText will be be displayed at the bottom of the visible list. If the
list of scrollable text fills the text area, then text is scrolled up and the
new string passed to GrowScrollText is displayed at the bottom of the text
area. Note that the list of scrollable text could be null, in which case the
first call to GrowScrollText will create the first line of scrollable text in
the current window (but a scrollable text window must have been opened,
otherwise GrowScrollText will not work properly).
GrowScrollText will operate on the current window with focus. If that window
is not a scrollable text window, no action is taken and an error code (-1) is
returned (i.e., even if the scrollable text array is null, the window MUST
have been opened with OpenScrollText and NOT with BlankWin). If the scrollable
text array for the current window is already full (i.e., it has MaxTextLines
lines of text already defined), no action is taken and an error code (-2) is
returned.
GrowScrollText can be used to give the user visual feedback as a long running
task progresses. Suppose you have a routine that searches for specific records
in a data base, and you want to display these in a scrollable list for your
user to browse. GrowScrollText can be used to show the records being
dynamically added to the list as they are found. The following pseudo code
will implement this example:
Dim Text(1 to 1) AS STRING ' scrollable text, init as null
' The above array need only have 1 entry (initiallized to null).
' It's only used by OpenScrollWindow to initialize LangWin's data structure
' SaveText. Thereafter, the scrollable text is actually "grown" in SaveText.
.
.
w1=OpenScrollWindow( ..... ,Text(), .....) 'scrollable window with null text
' put some buttons in the window
.
.
DO WHILE AnyWinOpen ' process windows
wn=WinEvent(action) ' wait for an action
' process actions in windows
.
.
.
' assume that at this point you determine the need to search a
' database for records, and to display the results in a scrollable
' window (w1) where the user can browse the results.
' w1 is the number of the open window that will contain the
' scrollable text. at this point, the scrollable text array is null.
x=IsWinOpen(w1,Han) ' get handle of w1 and save in Han
' search loop
DO
' first, you must make sure window with text (w1) is current
CALL NewFocusWindow(Han) ' give focus to text window
rec$=GetNextMatch$(parms) ' get a record
IF rec$="" THEN EXIT DO ' see if search is done
' note: GetNextMatch$ is NOT part of LangWin. it's
' a fictitious function used to illustrate a routine
' that you might call to get a record and return in text string.
' i assume a null value is returned when search is completed.
rc=GrowScrollText(rec$) ' display rec in window
SELECT CASE rc ' test above return code
CASE -1 ' current window is not a scrollable text window
' process the error
CASE -2 ' scrollable text array is full
' process the error. this might include allowing
' the user to browse the scrollable text and waiting
' for a CONTINUE button to be clicked. when this
' button is clicked, you could close the window,
' re-open it (to clear text), and continue the search.
END SELECT
' using the time-out techniques from Section 6.22, you could
' also include a STOP button to interrupt the search.
' assume handle of STOP button is: Stop1
aa=-999 ' set time out option
x=WinEvent(aa) ' wait for 0.5 sec for an event
' loop until STOP button is clicked
LOOP UNTIL (aa=3 AND WinParms(CurWinPtr,16)=Stop1)
See SAMPLE05.BAS for actual code that used GrowScrollText.
------------------
7.0 MOUSE ROUTINES
------------------
LangWin has several general mouse routines that can be used to control the
mouse and get status. These are documented in WINHELP.BAS and include:
MouseExists - test to see if mouse driver is installed
InitMouse - initialize the mouse
GetButtonPress - get position of mouse when a button was last clicked
GetMousePos - get current position of mouse
SetMousePos - set current position of mouse
SetXLimit - set horizontal limits of mouse
SetYLimit - set vertical limits of mouse
ShowMouseCursor - make the mouse pointer visible
HideMouseCursor - hide the mouse pointer
SAMPLE03.BAS contains sample code using the above routines. Load WINHELP.BAS
into QB and hit F2 to see details on each of these routines.
-------------------------------
8.0 DIRECTORY AND FILE ROUTINES
-------------------------------
LangWin 2.0 has several functions that allow you to easily determine the
current drive and directory, set the current drive and directory, and extract a
list of files and sub-directories from the current directory. These are
documented in WINHELP.BAS and include:
ChangeDir - change the default directory on a specified drive
ChangeDrive - change the default drive
GetCurDir$ - get the default directory on a specified drive
GetCurDrive$ - get the default drive letter
GetFileNames - get either file names or sub-dir names from the
current directory and save them in a string array
SAMPLE04.BAS contains code that use these functions.
----------------------
9.0 COMING ATTRACTIONS
----------------------
With LangWin 2.1, you now have a professional quality toolkit for creating a
GUI in QuickBASIC. However, screen design with LangWin does leave a lot to be
desired. You have to manually figure out all of the screen coordinates for your
windows, and remember which parameters in what functions to use. LangWin
now has the ability to load WINHELP.BAS, so at least you can review the
parameter lists while you are developing. Still, it's a labor intensive process
to design the screen.
I am considering an graphical "front end" program that will allow you to design
your screen interactively using the mouse. You'll click on the coordinates
where windows are to be created, select colors, select objects from a menu and
place them into the window. When you are satisfied, the "front end" will then
generate the appropriate CALLs to LangWin routines. If you've read about or
seen VBDOS, this should sound familiar. So, stay tuned for LangWin 3.0.
--------------------------
10.0 CONTACTING THE AUTHOR
--------------------------
If you have any questions about LangWin, suggestions for improvement, or
want to report a bug (ugh), send U.S. Mail to:
Allen Lang
121 Windsor Commons
Cranbury, NJ 08512
If you have access to Prodigy, my ID is: KJNX76A
Before reporting a bug, I'll need to know your version of LangWin.
I've included a routine to extract the version number (GetVerNum$).
Go into QuickBASIC's immediate window and enter: print GetVerNum$
Before contacting me, I'd highly recommend that you: read ALL of the doc in
this file, read the descriptions for all LangWin's routines (load WINHELP.BAS
into QB and hit F2), and thoroughly examine the sample code included with this
distribution. While that will require an investment of your time, you'll be
able to fully utilize the power of LangWin, and you'll probably find the answer
to most of your questions. Good luck and happy coding!!!